I am creating several variables by parsing a long string into:
Year
Make
Model
Color
ColorLower
Style
Depending on the record I may have details in some or all of these variables. In most cases, though, some are blank. Following the variables being populated I add them into a database field that is the description of a car/vehicle.
Currently my if/else block goes one by one and if a variable has a non-zero length, the concatenated description variable
if (length($Year)>0)
{
$Description == $Description + " " + Year
}
elsif (length($Make) > 0)
$Description == $Description + " " + $Make
} ...and so on
What I have now is working, I'd be interested in hearing is there is a shorter, more compact way that I could maximize my code.
Thank you!
Better way is to use StringBuilder
using System;
using System.Text;
namespace stringy
{
class MainClass
{
public static void Main (string[] args)
{
string hello = "Hello World!";
int i = 123;
double d = 3.14;
StringBuilder sb = new StringBuilder();
sb.Append(hello);
sb.Append(i);
sb.Append(d);
Console.WriteLine (sb.ToString());
}
}
}
Adding strings includes lot of allocations and reallocations.
string Year = "2015", Make = "Ford", Model = "Rustbucket",
Color = "Red", ColorLower = "Green", Style = "Car";
string[] stuff = { Year, Make, Model, Color, ColorLower, Style };
string Description = "Start ";
Description+=String.Join(" ", stuff.Where(t => t != ""));
No pain! No gain! Checking, and memory optimization:
public class Entity{
public string Year {get;set;}
public string Make {get;set;}
public string Model {get;set;}
public string Color {get;set;}
public string ColorLower {get;set;}
public string Style {get;set;}
public string Description{
get {
string format = "{0} ";
StringBuilder sb = new StringBuilder();
sb.Append(!string.IsNullOrEmpty(Year) && !string.IsNullOrEmpty(Year.Trim())
? string.Format(format, Year.Trim()) : string.Empty);
sb.Append(!string.IsNullOrEmpty(Make) && !string.IsNullOrEmpty(Make.Trim())
? string.Format(format, Make.Trim()) : string.Empty);
sb.Append(!string.IsNullOrEmpty(Model) && !string.IsNullOrEmpty(Model.Trim())
? string.Format(format, Model.Trim()) : string.Empty);
sb.Append(!string.IsNullOrEmpty(Color) && !string.IsNullOrEmpty(Color.Trim())
? string.Format(format, Color.Trim()) : string.Empty);
sb.Append(!string.IsNullOrEmpty(ColorLower) && !string.IsNullOrEmpty(ColorLower.Trim())
? string.Format(format, ColorLower.Trim()) : string.Empty);
sb.Append(!string.IsNullOrEmpty(Style) && !string.IsNullOrEmpty(Style.Trim())
? string.Format(format.Trim(), Style.Trim()) : string.Empty);
return sb.ToString();
}
}
}
static void Main()
{
var e1 = new Entity{Year="Y",Make="M",Model="Md",
Color="C",ColorLower="CL",Style="S"};
Console.WriteLine(e1.Description); // 'Y M Md C CL S'
}
I need to perform multi string replacement. I have a string where several parts need to be changed according to substitution map.
All replacement must be done in one action - it means if "a" should be replaced with "b" and also "b" must be replaced with "c" and input string is "abc", the result will be "bcc"
I have a sollution based on building regex and then replacing all matches. I wrote it some time ago, now I'm refactoring the code and not so satisfied with it. Is there better (faster, simplier) sollution?
This is what I have:
public static string Replace(string s, Dictionary<string, string> substitutions)
{
string pattern = "";
int i = 0;
foreach (string ch in substitutions.Keys)
{
if (i == 0)
pattern += "(" + Regex.Escape(ch) + ")";
else
pattern += "|(" + Regex.Escape(ch) + ")";
i++;
}
var r = new Regex(pattern);
var parts = r.Split(s);
string ret = "";
foreach (string part in parts)
{
if (part.Length == 1 && substitutions.ContainsKey(part[0].ToString()))
{
ret += substitutions[part[0].ToString()];
}
else
{
ret += part;
}
}
return ret;
}
And test case:
var test = "test aabbcc";
var output = Replace(test, new Dictionary<string, string>{{"a","b"},{"b","y"}});
Assert.That(output=="test bbyycc");
You can replace all this with
var r = new Regex(string.Join("|", substitutions.Keys.Select(k => "(" + k + ")")));
return r.Replace(s, m => substitutions[m.Value]);
The key things are making use of the string.Join method rather than implementing it yourself, and making use of this Regex.Replace overload and delegates to do the replacement.
I have the following:
string test = "CustomerNumber";
or
string test2 = "CustomerNumberHello";
the result should be:
string result = "Customer";
The first word from the string is the result, the first word goes until the first uppercase letter, here 'N'
I already tried some things like this:
var result = string.Concat(s.Select(c => char.IsUpper(c) ? " " + c.ToString() : c.ToString()))
.TrimStart();
But without success, hope someone could offer me a small and clean solution (without RegEx).
The following should work:
var result = new string(
test.TakeWhile((c, index) => index == 0 || char.IsLower(c)).ToArray());
You could just go through the string to see which values (ASCII) are below 97 and remove the end. Not the prettiest or LINQiest way, but it works...
string test2 = "CustomerNumberHello";
for (int i = 1; i < test2.Length; i++)
{
if (test2[i] < 97)
{
test2 = test2.Remove(i, test2.Length - i);
break;
}
}
Console.WriteLine(test2); // Prints Customer
Try this
private static string GetFirstWord(string source)
{
return source.Substring(0, source.IndexOfAny("ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToArray(), 1));
}
Z][a-z]+ regex it will split the string to string that start with big letters her is an example
regex = "[A-Z][a-z]+";
MatchCollection mc = Regex.Matches(richTextBox1.Text, regex);
foreach (Match match in mc)
if (!match.ToString().Equals(""))
Console.writln(match.ToString() + "\n");
I have tested, this works:
string cust = "CustomerNumberHello";
string[] str = System.Text.RegularExpressions.Regex.Split(cust, #"[a-z]+");
string str2 = cust.Remove(cust.IndexOf(str[1], 1));
I wonder if it's possible to use split to devide a string with several parts that are separated with a comma, like this:
title, genre, director, actor
I just want the first part, the title of each string and not the rest?
string valueStr = "title, genre, director, actor";
var vals = valueStr.Split(',')[0];
vals will give you the title
Actually, there is a better way to do it than split:
public string GetFirstFromSplit(string input, char delimiter)
{
var i = input.IndexOf(delimiter);
return i == -1 ? input : input.Substring(0, i);
}
And as extension methods:
public static string FirstFromSplit(this string source, char delimiter)
{
var i = source.IndexOf(delimiter);
return i == -1 ? source : source.Substring(0, i);
}
public static string FirstFromSplit(this string source, string delimiter)
{
var i = source.IndexOf(delimiter);
return i == -1 ? source : source.Substring(0, i);
}
Usage:
string result = "hi, hello, sup".FirstFromSplit(',');
Console.WriteLine(result); // "hi"
You can do it:
var str = "Doctor Who,Fantasy,Steven Moffat,David Tennant";
var title = str.Split(',').First();
Also you can do it this way:
var index = str.IndexOf(",");
var title = index < 0 ? str : str.Substring(0, index);
These are the two options I managed to build, not having the luxury of working with var type, nor with additional variables on the line:
string f = "aS.".Substring(0, "aS.".IndexOf("S"));
Console.WriteLine(f);
string s = "aS.".Split("S".ToCharArray(),StringSplitOptions.RemoveEmptyEntries)[0];
Console.WriteLine(s);
This is what it gets:
Instead of using {0} {1}, etc. I want to use {title} instead. Then fill that data in somehow (below I used a Dictionary). This code is invalid and throws an exception. I wanted to know if i can do something similar to what i want. Using {0 .. N} is not a problem. I was just curious.
Dictionary<string, string> d = new Dictionary<string, string>();
d["a"] = "he";
d["ba"] = "llo";
d["lol"] = "world";
string a = string.Format("{a}{ba}{lol}", d);
No, but this extension method will do it
static string FormatFromDictionary(this string formatString, Dictionary<string, string> valueDict)
{
int i = 0;
StringBuilder newFormatString = new StringBuilder(formatString);
Dictionary<string, int> keyToInt = new Dictionary<string,int>();
foreach (var tuple in valueDict)
{
newFormatString = newFormatString.Replace("{" + tuple.Key + "}", "{" + i.ToString() + "}");
keyToInt.Add(tuple.Key, i);
i++;
}
return String.Format(newFormatString.ToString(), valueDict.OrderBy(x => keyToInt[x.Key]).Select(x => x.Value).ToArray());
}
Check this one, it supports formating:
public static string StringFormat(string format, IDictionary<string, object> values)
{
var matches = Regex.Matches(format, #"\{(.+?)\}");
List<string> words = (from Match matche in matches select matche.Groups[1].Value).ToList();
return words.Aggregate(
format,
(current, key) =>
{
int colonIndex = key.IndexOf(':');
return current.Replace(
"{" + key + "}",
colonIndex > 0
? string.Format("{0:" + key.Substring(colonIndex + 1) + "}", values[key.Substring(0, colonIndex)])
: values[key] == null ? string.Empty : values[key].ToString());
});
}
How to use:
string format = "{foo} is a {bar} is a {baz} is a {qux:#.#} is a really big {fizzle}";
var dictionary = new Dictionary<string, object>
{
{ "foo", 123 },
{ "bar", true },
{ "baz", "this is a test" },
{ "qux", 123.45 },
{ "fizzle", DateTime.Now }
};
StringFormat(format, dictionary)
You can implement your own:
public static string StringFormat(string format, IDictionary<string, string> values)
{
foreach(var p in values)
format = format.Replace("{" + p.Key + "}", p.Value);
return format;
}
Phil Haack discussed several methods of doing this on his blog a while back: http://haacked.com/archive/2009/01/14/named-formats-redux.aspx. I've used the "Hanselformat" version on two projects with no complaints.
It's possible now
With Interpolated Strings of C# 6.0 you can do this:
string name = "John";
string message = $"Hi {name}!";
//"Hi John!"
static public class StringFormat
{
static private char[] separator = new char[] { ':' };
static private Regex findParameters = new Regex(
"\\{(?<param>.*?)\\}",
RegexOptions.Compiled | RegexOptions.Singleline);
static string FormatNamed(
this string format,
Dictionary<string, object> args)
{
return findParameters.Replace(
format,
delegate(Match match)
{
string[] param = match.Groups["param"].Value.Split(separator, 2);
object value;
if (!args.TryGetValue(param[0], out value))
value = match.Value;
if ((param.Length == 2) && (param[1].Length != 0))
return string.Format(
CultureInfo.CurrentCulture,
"{0:" + param[1] + "}",
value);
else
return value.ToString();
});
}
}
A little more involved than the other extension method, but this should also allow non-string values and formatting patterns used on them, so in your original example:
Dictionary<string, object> d = new Dictionary<string, object>();
d["a"] = DateTime.Now;
string a = string.FormatNamed("{a:yyyyMMdd-HHmmss}", d);
Will also work...
Since C# 6 released you are able to use String Interpolation feature
Code that solves your question:
string a = $"{d["a"]}{d["ba"]}{d["lol"]}";
Why a Dictionary? It's unnecessary and overly complicated. A simple 2 dimensional array of name/value pairs would work just as well:
public static string Format(this string formatString, string[,] nameValuePairs)
{
if (nameValuePairs.GetLength(1) != 2)
{
throw new ArgumentException("Name value pairs array must be [N,2]", nameof(nameValuePairs));
}
StringBuilder newFormat = new StringBuilder(formatString);
int count = nameValuePairs.GetLength(0);
object[] values = new object[count];
for (var index = 0; index < count; index++)
{
newFormat = newFormat.Replace(string.Concat("{", nameValuePairs[index,0], "}"), string.Concat("{", index.ToString(), "}"));
values[index] = nameValuePairs[index,1];
}
return string.Format(newFormat.ToString(), values);
}
Call the above with:
string format = "{foo} = {bar} (really, it's {bar})";
string formatted = format.Format(new[,] { { "foo", "Dictionary" }, { "bar", "unnecessary" } });
Results in: "Dictionary = unnecessary (really, it's unnecessary)"
public static string StringFormat(this string format, IDictionary<string, object> values)
{
return Regex.Matches(format, #"\{(?!\{)(.+?)\}")
.Select(m => m.Groups[1].Value)
.Aggregate(format, (current, key) =>
{
string[] splits = key.Split(":");
string replacement = splits.Length > 1
? string.Format($"{{0:{splits[1]}}}", values[splits[0]])
: values[key].ToString();
return Regex.Replace(current, "(.|^)("+ Regex.Escape($"{{{key}}}")+")(.|$)",
m => m.Groups[1].ToString() == "{" && m.Groups[3].ToString() == "}"
? m.Groups[2].ToString()
: m.Groups[1] + replacement + m.Groups[3]
);
});
}
This is similar to another answer, but it considers escaping with {{text}}.
I took #LPCRoy's answer and refactored for generic dictionary types, added parameter validation, it only replaces fields it can find, and attempts to solve the "double curly brace" issue by escaping them first, then replacing them at the end with single braces in the same way that string.Format() does.
public static string FormatFromDictionary<K, T>(this string formatString, IDictionary<K, T> valueDict)
{
if (string.IsNullOrWhiteSpace(formatString)) return formatString;
if (valueDict == null || !valueDict.Any()) return formatString;
bool escapedDoubleCurlyBraces = false;
if (formatString.Contains("{{") || formatString.Contains("}}"))
{
formatString = formatString.Replace("{{", "\\{\\{").Replace("}}", "\\}\\}");
escapedDoubleCurlyBraces = true;
}
int i = 0;
StringBuilder newFormatString = new StringBuilder(formatString);
Dictionary<K, int> keyToInt = new Dictionary<K, int>();
string field;
//StringBuilder.Replace() is faster than string.Replace().
foreach (var kvp in valueDict)
{
field = "{" + kvp.Key.ToString() + "}";
if (formatString.Contains(field))
{
newFormatString = newFormatString.Replace(field, "{" + i.ToString() + "}");
keyToInt.Add(kvp.Key, i);
i++;
}
}
//Any replacements to make?
if (keyToInt.Any())
{
formatString = string.Format(
newFormatString.ToString(),
keyToInt.OrderBy(kvp => kvp.Value).Select(kvp => (object)valueDict[kvp.Key]).ToArray()
);
}
if (escapedDoubleCurlyBraces)
{
//Converts "{{" and "}}" to single "{" and "}" for the final result just like string.format().
formatString = formatString.Replace("\\{\\{", "{").Replace("\\}\\}", "}");
}
return formatString;
}
Here is a nice solution that is very useful when formatting emails: http://www.c-sharpcorner.com/UploadFile/e4ff85/string-replacement-with-named-string-placeholders/
Edited:
public static class StringExtension
{
public static string Format( this string str, params Expression<Func<string,object>>[] args)
{
var parameters = args.ToDictionary( e=>string.Format("{{{0}}}",e.Parameters[0].Name), e=>e.Compile()(e.Parameters[0].Name));
var sb = new StringBuilder(str);
foreach(var kv in parameters)
{
sb.Replace( kv.Key, kv.Value != null ? kv.Value.ToString() : "");
}
return sb.ToString();
}
}
Example usage:
public string PopulateString(string emailBody)
{
User person = _db.GetCurrentUser();
string firstName = person.FirstName; // Peter
string lastName = person.LastName; // Pan
return StringExtension.Format(emailBody.Format(
firstname => firstName,
lastname => lastName
));
}
(your Dictionary + foreach + string.Replace) wrapped in a sub-routine or extension method?
Obviously unoptimized, but...