Interpolated strings stored in a variable [duplicate] - c#

Can one store the template of a string in a variable and use interpolation on it?
var name = "Joe";
var template = "Hi {name}";
I then want to do something like:
var result = $template;
The reason is my templates will come from a database.

I guess that these strings will have always the same number of parameters, even if they can change. For example, today template is "Hi {name}", and tomorrow could be "Hello {name}".
Short answer: No, you cannot do what you have proposed.
Alternative 1: use the string.Format method.
You can store in your database something like this:
"Hi {0}"
Then, when you retrieve the string template from the db, you can write:
var template = "Hi {0}"; //retrieved from db
var name = "Joe";
var result = string.Format(template, name);
//now result is "Hi Joe"
With 2 parameters:
var name2a = "Mike";
var name2b = "John";
var template2 = "Hi {0} and {1}!"; //retrieved from db
var result2 = string.Format(template2, name2a, name2b);
//now result2 is "Hi Mike and John!"
Alternative 2: use a placeholder.
You can store in your database something like this:
"Hi {name}"
Then, when you retrieve the string template from the db, you can write:
var template = "Hi {name}"; //retrieved from db
var name = "Joe";
var result = template.Replace("{name}", name);
//now result is "Hi Joe"
With 3 parameters:
var name2a = "Mike";
var name2b = "John";
var template2 = "Hi {name2a} and {name2b}!"; //retrieved from db
var result2 = template2
.Replace("{name2a}", name2a)
.Replace("{name2b}", name2b);
//now result2 is "Hi Mike and John!"
Pay attention at which token you choose for your placeholders. Here I used surrounding curly brackets {}. You should find something that is unlikely to cause collisions with the rest of your text. And that depends entirely on your context.

This can be done as requested using dynamic compilation, such as through the Microsoft.CodeAnalysis.CSharp.Scripting package. For example:
var name = "Joe";
var template = "Hi {name}";
var result = await CSharpScript.EvaluateAsync<string>(
"var name = \"" + name + "\"; " +
"return $\"" + template + "\";");
Note that this approach is slow, and you'd need to add more logic to handle escaping of quotes (and injection attacks) within strings, but the above serves as a proof-of-concept.

No you can't do that since it needs name value at the time string is created (compile time). Consider using String.Format or String.Replace instead.

I just had the same need in my app so will share my solution using String.Replace(). If you're able to use LINQ then you can use the Aggregate method (which is a reducing function, if you're familiar with functional programming) combined with a Dictionary that provides the substitutions you want.
string template = "Hi, {name} {surname}";
Dictionary<string, string> substitutions = new Dictionary<string, string>() {
{ "name", "Joe" },
{ "surname", "Bloggs" },
};
string result = substitutions.Aggregate(template, (args, pair) =>
args.Replace($"{{{pair.Key}}}", pair.Value)
);
// result == "Hi, Joe Bloggs"
This works by starting with the template and then iterating over each item in the substitution dictionary, replacing the occurrences of each one. The result of one Replace() call is fed into the input to the next, until all substitutions are performed.
The {{{pair.Key}}} bit is just to escape the { and } used to find a placeholder.

This is pretty old now, but as I've just come across it it's new to me!
It's a bit overkill for what you need, but I have used Handlebars.NET for this sort of thing.
You can create quite complex templates and merge in hierarchical data structures for the context. There's rules for looping and conditional sections, partial template compositing and even helper function extension points. It also handles many data types gracefully.
There's way too much to go into here, but a short example to illustrate...
var source = #"Hello {{Guest.FirstName}}{{#if Guest.Surname}} {{Guest.Surname}}{{/if}}!";
var template = Handlebars.Compile(source);
var rec = new {
Guest = new { FirstName = "Bob", Surname = null }
};
var resultString = template(rec);
In this case the surname will only be included in the output if the value is not null or empty.
Now admittedly this is more complicated for users than simple string interpolation, but remember that you can still just use {{fieldName}} if you want to, just that you can do lots more as well.
This particular nuGet is a port of HandlebarsJs so it has a high degree of compatibility. HandlebarsJs is itself a port of Mustache - there are direct dotNet ports of Mustache but IMHO HandlebarsNET is the business.

Related

Check if a string starts with a possible number of strings in C#?

How can I most efficiently check to see if an input string starts with a string that belongs in a list of strings?
For example possiblePrefixes = "1234", "1235", "1236". If input = "1235av2425" should return true. If input = "1237352ko" should return false.
you can use Any for this. the concept here is you need to check whether there is any item in the list which is the prefix for the given string.
List<string> list = new List<string>() { "1234", "1235", "1236" };
string input = "1237352ko";
var exisits = list.Any(x => input.StartsWith(x)); //returns false
when string input = "1235av2425"; it will return true
An efficient datastructure for this type of search would be a prefix tree (aka "Trie").
For your example data such a tree might look something like this:
123
|-4
|-5
|-6
This could allow a lookup time that is independent of the number of prefixes you want to check against.
But as far as I know there are no builtin types for this, so you would either need to find a library, or implement it yourself.
The solution using Any and StartsWith will be the best in most cases. Looking for an optimized solution will only be necessary if you have a long list of possible prefixes and/or a long list of texts to check against the same prefixes.
In that case, using a pre-compiled regular expression built once from the list of possible prefixes and then re-used for multiple checks might be a little faster.
// Build regular expression once
string[] possiblePrefixes = new string[] { "1234", "1235", "1236" };
var escaped = possiblePrefixes.Select(p => Regex.Escape(p));
string pattern = "^(" + string.Join("|", escaped) + ").*";
Regex regEx = new Regex(pattern, RegexOptions.Compiled);
// Now use it multiple times
string input = "1235av2425";
bool result = regEx.IsMatch(input);
Following are the 2 solutions
Solution # 1 (using Lambda Expression)
List<string> possiblePrefixes = new List<string>() { "1234", "1235", "1236" };
string input = "1235av2425";
var result = possiblePrefixes.Any(x => input.StartsWith(x));
Console.WriteLine(result); //returns True
Solution # 2 (using SQL)
List<string> possiblePrefixes = new List<string>() { "1234", "1235", "1236" };
string input = "1235av2425";
var result = (from val in possiblePrefixes
where input.StartsWith(val)
select val).Any();
Console.WriteLine(result); //returns True

Iterating whole list and updating string using ToLower - Not working

Technologies using.
C#
.NET 4.0
Visual Studio 2010
Problem.
I have a List<User> which contains an Email property. I want to lowercase all the email addresses within the list, but my implementation is not working. I'm using the following statement:
emails.ToList().ForEach(e => e.ToLower());
This didnt work at all for email addresses like Catherine.Burke#email.co.uk. I built the following to test this:
string email = "Catherine.Burke#email.co.uk";
email = email.ToLower();
Console.WriteLine("Email: " + email);
string email2 = "Catherine.Burke#email.co.uk";
string email3 = "Gareth.bradley#email.co.uk";
List<string> emails = new List<string>();
emails.Add(email2);
emails.Add(email3);
emails.ToList().ForEach(e => e.ToLower());
emails.ToList().ForEach(delegate(string e)
{
Console.WriteLine("ForEach deletegate : " + e);
});
List<EmailAddress> emailAddresses = new List<EmailAddress>();
emailAddresses.Add(new EmailAddress { FullAddress = "Catherine.Burke#email.co.uk" });
emailAddresses.Add(new EmailAddress { FullAddress = "Gareth.bradley#email.co.uk" });
emailAddresses.ToList().ForEach(e => e.FullAddress.ToLower());
emailAddresses.ToList().ForEach(delegate(EmailAddress e)
{
Console.WriteLine("EmailAddress delegate: " + e.FullAddress);
});
foreach (EmailAddress em in emailAddresses)
{
Console.WriteLine("Foreach Print: " + em.FullAddress);
}
Now I thought it might be the Culture and as these are names, it kept them uppercase, but when I used ToLower() on a singular string it worked. The above ran with the following output, as you can see the 1st line shows an email address with lowercase characters, whereas the implementation of the various List's I tried using ForEach() have not worked. I'm presuming my implementation of ForEach() is incorrect?
Making my comment an answer as requested:
Use a simple for-loop. List.ForEach is a method where you get the string as argument, you can't replace the whole reference there and since strings are immutable you can't change them either. You have to reassign the string returned from String.ToLower to your variable:
for(int i = 0; i < emails.Count; i++)
emails[i] = emails[i].ToLower();
Side-note: if you are making all emails lowercase to get a case-insensitive comparison it's better to use the String.Equals overload with the right StringComparison
string email1 = "Catherine.Burke#email.co.uk";
string email2 = "catherine.burke#email.co.uk";
if (String.Equals(email1, email2, StringComparison.InvariantCultureIgnoreCase))
{
// ...
}
emails.ToList().ForEach(e => e.ToLower()); does just call ToLower() but does not assign the result.
What you want is:
var lowerEmails = emails.Select(e => e.ToLower()).ToList();
Try this:
emailAddresses.ToList().ForEach(e => e.FullAddress = e.FullAdress.ToLower());
As weertzui altready mentions the ForEach-method simply calls the delegate. However the result of this action is not used in your code in any way.
However I´d strongly recommend to use a simply foreach:
foreach(var mail in emailadresses) mail.FullAdress = mail.FullAdress.ToLower();
which seems better readable to me.

Replace {x} tokens in strings

We have a template URL like:
http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}
and I have values for these constant tokens. How can replace all these tokens in C#?
A simple approach is to use a foreach and a Dictionary with a String.Replace:
var values = new Dictionary<string, string> {
{ "{networkid}", "WHEEE!!" }
// etc.
};
var url = "http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}";
foreach(var key in values.Keys){
url = url.Replace(key,values[key]);
}
There is no standard way to "replace with dictionary values" in .NET. While there are a number of template engines, it's not very hard to write a small solution for such an operation. Here is an example which runs in LINQPad and utilizes a Regular Expression with a Match Evaluator.
As the result is a URL,
it is the callers responsibility to make sure all the supplied values are correctly encoded. I recommend using Uri.EscapeDataString as appropriate .. but make sure to not double-encode, if it is processed elsewhere.
Additionally, the rules of what to do when no replacement is found should be tailored to need. If not-found replacements should be eliminated entirely along with the query string key, the following can expand the regular expression to #"\w+=({\w+})" to also capture the parameter key in this specific template situation.
string ReplaceUsingDictionary (string src, IDictionary<string, object> replacements) {
return Regex.Replace(src, #"{(\w+)}", (m) => {
object replacement;
var key = m.Groups[1].Value;
if (replacements.TryGetValue(key, out replacement)) {
return Convert.ToString(replacement);
} else {
return m.Groups[0].Value;
}
});
}
void Main()
{
var replacements = new Dictionary<string, object> {
{ "networkid", "WHEEE!!" }
// etc.
};
var src = "http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}";
var res = ReplaceUsingDictionary(src, replacements);
// -> "http://api.example.com/sale?..&networkid=WHEEE!!&..&pageid={pageid}&..
res.Dump();
}
More advanced techniques, like reflection and transforms, are possible - but those should be left for the real template engines.
I am guessing you are trying to replace parameters in url with your values. This can be done using C# HttpUtility.ParseQueryString
Get the CurrentURL from
var _myUrl = System.Web.HttpUtility.ParseQueryString(Request.RawUrl);
Read Parameter from your Query string
string value1 = _myUrl ["networkid"];
Write a value into the QueryString object.
_myUrl ["networkid"] = "Your Value";
and then finally turn it back into URL
var _yourURIBuilder= new UriBuilder(_myUrl );
_myUrl = _yourURIBuilder.ToString();
You can use this alos using LinQ
Dictionary<string, string> myVal = new Dictionary<string, string>();
myVal.Add("networkid", "1");
myVal.Add("pageid", "2");
myVal.Add("master", "3");
myVal.Add("optinfo", "4");
myVal.Add("publisher", "5");
myVal.Add("userId", "6");
string url = #"http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}";
myVal.Select(a => url = url.Replace(string.Concat("{", a.Key, "}"), a.Value)).ToList();
this line can do your required functionlity
myVal.Select(a => url = url.Replace(string.Concat("{", a.Key, "}"), a.Value)).ToList();
There is a Nuget called StringTokenFormatter that does this well
https://www.nuget.org/packages/StringTokenFormatter/
Regex.Replace makes a single pass over a template string, offering you an opportunity to replace matched expressions. Use it by creating an regular expression that matches any token. Then look up replacement values for the tokens in a dictionary.
static string ReplaceTokens(string template, Dictionary<string, string> replacements) =>
Regex.Replace(template, #"{(\w+)}",
match => replacements.TryGetValue(match.Groups[1].Value, out string replacement) ? replacement : match.Value);
The algorithm completes in time linear with the size of the template string and the replacement strings, so O(t + r). Beware of algorithms that make multiple passes. They run slowly in time O(t * r) and will give incorrect results if one of the replacement values contains a token for later replacement. This unit test shows the pitfall:
public void TestReplaceTokens() {
var replacements = new Dictionary<string, string> {
["Movement"] = "drive {DontReplace}",
["DontReplace"] = "Should not appear"
};
string withReplacements = ReplaceTokens("I always {Movement} in {Direction}.", replacements);
Assert.AreEqual("I always drive {DontReplace} in {Direction}.", withReplacements);
}

Variable in the dynamic string in C#

I have a module to send message with the SMS. I can put the variable in the string if the message is a static, but the user request the message can be changed whatever their want.
I created this variable
CompanyName
CustomerName
BillNumber
Payment
Example :
From {Company}. Hi Mr/Mrs {CustomerName}, your bill number is
{BillNumber} with a total payment of {Payment}. We want to inform you
the items has been completed and ready for collection.
My current code is work for static message,
string messageSms = "From " +Company+ ". Hi Mr/Mrs "+{CustomerName}+", your bill number is "+{BillNumber}+" with a total payment of "+{Payment}+". We want to inform you the items has been completed and ready for collection.";
But how can be done with dynamic message? How can I detect the variable in the string and set the data on the variable?
I also following with this article but not help so much.
var newString = messageSms.Replace("{Company}", CompanyName)
.Replace("{CustomerName}", CustomerName) // ...etc
Should do it.
Assuming I'm understanding, i think the String.Inject class could be helpful. Picture a named String.Format:
"Hello, {company}!".Inject(new { company = "StackOverflow" });
// "Hello, StackOverflow!"
The other benefit is you can have a hard-coded model and reference direct properties of it. e.g.
class Contact
{
string FirstName;
string LastName;
}
String greeting = "Mr. {FirstName} {LastName}, Welcome to the site ...";
String result = greeting.Inject(new Contact
{
FirstName = "Brad",
LastName = "Christie"
});
You could also use interpolated strings using C# 6.0
var messageSms = $"From {companyName}. Hi {customerName}, your bill number is {billNumber} with a total payment of {payment}.";
I would approach with the following :
string Company;
string CustomerName;
string BillNumber;
string Payment;
string messageSms = $#"
From {Company}. Hi Mr/Mrs {CustomerName}, your bill number is {BillNumber}
with a total payment of {Payment}. We want to inform you the items has been
completed and ready for collection.
";
Try String.Format Method, for example:
string messageSms = String.Format("From {0}. Hi ..{1}, Your..{2} with..{3}. We..",
CompanyName, CustomerName, BillNumber, Payment);
I assume that the easiest way to achieve this (you did not clarify your question) is to use string.Format().
Just use it like this:
string company = ...;
string name= ...;
string billNr= ...;
string payment= ...;
string output = string.Format("From {0}. Hi Mr/Mrs {1}, your bill number is {2} with a total payment of {3}. We want to inform you the items has been completed and ready for collection.", company, name, billNr, payment);
Why not use a StringBuilder instead?
StringBuilder stringBuilder = new StringBuilder("From {Company}.Hi Mr/Mrs {CustomerName}, your bill number is {BillNumber} with a total payment of {Payment}. We want to inform you the items has been completed and ready for collection.");
stringBuilder.Replace("{Company}",CompanyName);
stringBuilder.Replace("{CustomerName}",CustomerName);
stringBuilder.Replace("{BillNumber}",BillNumber);
stringBuilder.Replace("{Payment}",Payment);
string messageSms = stringBuilder.ToString();
To make a reusable solution you could start by declaring an object that contains the replacement values as properties. In this case I simply declare an anonymous object but a normal class would work just as well:
var data = new {
Company = "Stack Overflow",
CustomerName = "Martin Liversage",
BillNumber = 123456,
Payment = 1234.567M.ToString("N2")
};
Notic how I "cheat" and assign a string to Payment. Number and date/time formatting is always a complex issue and I have decided to do the formatting up-front when I declare the data object. You could instead build some more or less elaborate formatting rules into the formatting engine.
Having a data object with properties I can build a dictionary of name/value pairs:
var dictionary = data
.GetType()
.GetProperties()
.ToDictionary(
propertyInfo => propertyInfo.Name,
propertyInfo => propertyInfo.GetValue(data, null)
);
Assuming that format contains the formatting template it is simply a matter of looping over the elements in the dictionary to create the replacement string:
var buffer = new StringBuilder(format);
foreach (var name in dictionary.Keys) {
var value = dictionary[name].ToString();
buffer.Replace("{" + name + "}", value);
}
var message = buffer.ToString();

Which approach to templating in C# should I take?

What I have
I have templates that are stored in a database, and JSON data that gets converted into a dictionary in C#.
Example: 
Template: "Hi {FirstName}"
Data: "{FirstName: 'Jack'}"
This works easily with one level of data by using a regular expression to pull out anything within {} in the template.
What I want
I would like to be able to go deeper in the JSON than the first layer.
Example:
Template: "Hi {Name: {First}}"
Data: "{Name: {First: 'Jack', Last: 'Smith'}}"
What approach should I be taking? (and some guidance on where to start with your pick)
A regular expression
Not use JSON in the template (in favor of xslt or something similar)
Something else
I'd also like to be able to loop through data in the template, but I have no idea at all where to start with that one!
Thanks heaps
You are in luck! SmartFormat does exactly as you describe. It is a lightweight, open-source string formatting utility.
It supports named placeholders:
var template = " {Name:{Last}, {First}} ";
var data = new { Name = new { First="Dwight", Last="Schrute" } };
var result = Smart.Format(template, data);
// Outputs: " Schrute, Dwight " SURPRISE!
It also supports list formatting:
var template = " {People:{}|, |, and} ";
var data = new { People = new[]{ "Dwight", "Michael", "Jim", "Pam" } };
var result = Smart.Format(template, data);
// Outputs: " Dwight, Michael, Jim, and Pam "
You can check out the unit tests for Named Placeholders and List Formatter to see plenty more examples!
It even has several forms of error-handling (ignore errors, output errors, throw errors).
Note: the named placeholder feature uses reflection and/or dictionary lookups, so you can deserialize the JSON into C# objects or nested Dictionaries, and it will work great!
Here is how I would do it:
Change your template to this format Hi {Name.First}
Now create a JavaScriptSerializer to convert JSON in Dictionary<string, object>
JavaScriptSerializer jss = new JavaScriptSerializer();
dynamic d = jss.Deserialize(data, typeof(object));
Now the variable d has the values of your JSON in a dictionary.
Having that you can run your template against a regex to replace {X.Y.Z.N} with the keys of the dictionary, recursively.
Full Example:
public void Test()
{
// Your template is simpler
string template = "Hi {Name.First}";
// some JSON
string data = #"{""Name"":{""First"":""Jack"",""Last"":""Smith""}}";
JavaScriptSerializer jss = new JavaScriptSerializer();
// now `d` contains all the values you need, in a dictionary
dynamic d = jss.Deserialize(data, typeof(object));
// running your template against a regex to
// extract the tokens that need to be replaced
var result = Regex.Replace(template, #"{?{([^}]+)}?}", (m) =>
{
// Skip escape values (ex: {{escaped value}} )
if (m.Value.StartsWith("{{"))
return m.Value;
// split the token by `.` to run against the dictionary
var pieces = m.Groups[1].Value.Split('.');
dynamic value = d;
// go after all the pieces, recursively going inside
// ex: "Name.First"
// Step 1 (value = value["Name"])
// value = new Dictionary<string, object>
// {
// { "First": "Jack" }, { "Last": "Smith" }
// };
// Step 2 (value = value["First"])
// value = "Jack"
foreach (var piece in pieces)
{
value = value[piece]; // go inside each time
}
return value;
});
}
I didn't handle exceptions (e.g. the value couldn't be found), you can handle this case and return the matched value if it wasn't found. m.Value for the raw value or m.Groups[1].Value for the string between {}.
Have you thought of using Javascript as your scripting language? I had great success with Jint, although the startup cost is high. Another option is Jurassic, which I haven't used myself.
If you happen to have a Web Application, using Razor maybe an idea, see here.
Using Regex or any sort of string parsing can certainly work for trivial things, but can get painful when you want logic or even just basic hierarchies. If you deserialize your JSON into nested Dictionaries, you can build a parser relatively easily:
// Untested and error prone, just to illustrate the concept
var parts = "parentObj.childObj.property".split('.');
Dictionary<object,object> current = yourDeserializedObject;
foreach(var key in parts.Take(parts.Length-1)){
current = current[key];
}
var value = current[parts.Last()];
Just whatever you do, don't do XSLT. Really, if XSLT is the answer then the question must have been really desperate :)
Why not us nvelocity or something?

Categories