Parse URL array parameters into C# Dictionaries - c#

I have the following code to parse key value pairs from URLs:
public static NameValueCollection ParseQueryString(String query)
{
NameValueCollection queryParameters = new NameValueCollection();
string[] querySegments = query.Split('&');
foreach (string segment in querySegments)
{
string[] parts = segment.Split('=');
if (parts.Length > 0)
{
string key = parts[0].Trim(new char[] { '?', ' ' });
string val = parts[1].Trim();
queryParameters.Add(key, val);
}
}
return queryParameters;
}
I am using this function like this:
args = ParseQueryString("alpha=1&beta=bbbb&array%5B0%5D%5Ba%5D=1&array%5B0%5D%5Bb%5D=2&array%5B1%5D%5Ba%5D=1&array%5B1%5D%5Bb%5D=2&array%5B%5D=3&array%5B%5D=4");
foreach (var k in args.AllKeys)
{
tw.WriteLine(k + ": " + args[k]);
}
Output:
alpha: 1
beta: bbbb
array[0][a]: 1
array[0][b]: 2
array[1][a]: 1
array[1][b]: 2
array[]: 3,4
I need an output of nested NameValueCollections or nested Dictionaries, so I can access the values somethis like this:
args = ParseQueryString("alpha=1&beta=bbbb&array%5B0%5D%5Ba%5D=1&array%5B0%5D%5Bb%5D=2&array%5B1%5D%5Ba%5D=1&array%5B1%5D%5Bb%5D=2&array%5B%5D=3&array%5B%5D=4");
var item = args.Get("array").Get(0).Get("b"); // will be "2"
What is the most elegant method to achieve this? I would prefer a solution without System.Web or any extra reference.

If you use the ASP.NET MVC Framework, the default MVC parameter binder can deal with it automatically in the following way:
// GetData?filters[0][field]=fieldName&filters[0][type]=number&filters[0][value]=3
public ActionResult GetData(IEnumerable<Dictionary<string,string>> filters)
{
// todo
}
For more info and examples check out this blog post that I wrote on this topic.

Related

Easier way to extract ID from a string? [duplicate]

I would like to parse a string such as p1=6&p2=7&p3=8 into a NameValueCollection.
What is the most elegant way of doing this when you don't have access to the Page.Request object?
There's a built-in .NET utility for this: HttpUtility.ParseQueryString
// C#
NameValueCollection qscoll = HttpUtility.ParseQueryString(querystring);
' VB.NET
Dim qscoll As NameValueCollection = HttpUtility.ParseQueryString(querystring)
You may need to replace querystring with new Uri(fullUrl).Query.
HttpUtility.ParseQueryString will work as long as you are in a web app or don't mind including a dependency on System.Web. Another way to do this is:
NameValueCollection queryParameters = new NameValueCollection();
string[] querySegments = queryString.Split('&');
foreach(string segment in querySegments)
{
string[] parts = segment.Split('=');
if (parts.Length > 0)
{
string key = parts[0].Trim(new char[] { '?', ' ' });
string val = parts[1].Trim();
queryParameters.Add(key, val);
}
}
A lot of the answers are providing custom examples because of the accepted answer's dependency on System.Web. From the Microsoft.AspNet.WebApi.Client NuGet package there is a UriExtensions.ParseQueryString, method that can also be used:
var uri = new Uri("https://stackoverflow.com/a/22167748?p1=6&p2=7&p3=8");
NameValueCollection query = uri.ParseQueryString();
So if you want to avoid the System.Web dependency and don't want to roll your own, this is a good option.
I wanted to remove the dependency on System.Web so that I could parse the query string of a ClickOnce deployment, while having the prerequisites limited to the "Client-only Framework Subset".
I liked rp's answer. I added some additional logic.
public static NameValueCollection ParseQueryString(string s)
{
NameValueCollection nvc = new NameValueCollection();
// remove anything other than query string from url
if(s.Contains("?"))
{
s = s.Substring(s.IndexOf('?') + 1);
}
foreach (string vp in Regex.Split(s, "&"))
{
string[] singlePair = Regex.Split(vp, "=");
if (singlePair.Length == 2)
{
nvc.Add(singlePair[0], singlePair[1]);
}
else
{
// only one key with no value specified in query string
nvc.Add(singlePair[0], string.Empty);
}
}
return nvc;
}
To do this without System.Web, without writing it yourself, and without additional NuGet packages:
Add a reference to System.Net.Http.Formatting
Add using System.Net.Http;
Use this code:
new Uri(uri).ParseQueryString()
https://msdn.microsoft.com/en-us/library/system.net.http.uriextensions(v=vs.118).aspx
I needed a function that is a little more versatile than what was provided already when working with OLSC queries.
Values may contain multiple equal signs
Decode encoded characters in both name and value
Capable of running on Client Framework
Capable of running on Mobile Framework.
Here is my solution:
Public Shared Function ParseQueryString(ByVal uri As Uri) As System.Collections.Specialized.NameValueCollection
Dim result = New System.Collections.Specialized.NameValueCollection(4)
Dim query = uri.Query
If Not String.IsNullOrEmpty(query) Then
Dim pairs = query.Substring(1).Split("&"c)
For Each pair In pairs
Dim parts = pair.Split({"="c}, 2)
Dim name = System.Uri.UnescapeDataString(parts(0))
Dim value = If(parts.Length = 1, String.Empty,
System.Uri.UnescapeDataString(parts(1)))
result.Add(name, value)
Next
End If
Return result
End Function
It may not be a bad idea to tack <Extension()> on that too to add the capability to Uri itself.
If you don't want the System.Web dependency, just paste this source code from HttpUtility class.
I just whipped this together from the source code of Mono. It contains the HttpUtility and all it's dependencies (like IHtmlString, Helpers, HttpEncoder, HttpQSCollection).
Then use HttpUtility.ParseQueryString.
https://gist.github.com/bjorn-ali-goransson/b04a7c44808bb2de8cca3fc9a3762f9c
If you want to avoid the dependency on System.Web that is required to use HttpUtility.ParseQueryString, you could use the Uri extension method ParseQueryString found in System.Net.Http.
Make sure to add a reference (if you haven't already) to System.Net.Http in your project.
Note that you have to convert the response body to a valid Uri so that ParseQueryString (in System.Net.Http)works.
string body = "value1=randomvalue1&value2=randomValue2";
// "http://localhost/query?" is added to the string "body" in order to create a valid Uri.
string urlBody = "http://localhost/query?" + body;
NameValueCollection coll = new Uri(urlBody).ParseQueryString();
I just realized that Web API Client has a ParseQueryString extension method that works on a Uri and returns a HttpValueCollection:
var parameters = uri.ParseQueryString();
string foo = parameters["foo"];
private void button1_Click( object sender, EventArgs e )
{
string s = #"p1=6&p2=7&p3=8";
NameValueCollection nvc = new NameValueCollection();
foreach ( string vp in Regex.Split( s, "&" ) )
{
string[] singlePair = Regex.Split( vp, "=" );
if ( singlePair.Length == 2 )
{
nvc.Add( singlePair[ 0 ], singlePair[ 1 ] );
}
}
}
Just access Request.QueryString. AllKeys mentioned as another answer just gets you an array of keys.
HttpUtility.ParseQueryString(Request.Url.Query) return is HttpValueCollection (internal class). It inherits from NameValueCollection.
var qs = HttpUtility.ParseQueryString(Request.Url.Query);
qs.Remove("foo");
string url = "~/Default.aspx";
if (qs.Count > 0)
url = url + "?" + qs.ToString();
Response.Redirect(url);
Since everyone seems to be pasting his solution.. here's mine :-)
I needed this from within a class library without System.Web to fetch id parameters from stored hyperlinks.
Thought I'd share because I find this solution faster and better looking.
public static class Statics
public static Dictionary<string, string> QueryParse(string url)
{
Dictionary<string, string> qDict = new Dictionary<string, string>();
foreach (string qPair in url.Substring(url.IndexOf('?') + 1).Split('&'))
{
string[] qVal = qPair.Split('=');
qDict.Add(qVal[0], Uri.UnescapeDataString(qVal[1]));
}
return qDict;
}
public static string QueryGet(string url, string param)
{
var qDict = QueryParse(url);
return qDict[param];
}
}
Usage:
Statics.QueryGet(url, "id")
Hit up Request.QueryString.Keys for a NameValueCollection of all query string parameters.
To get all Querystring values try this:
Dim qscoll As NameValueCollection = HttpUtility.ParseQueryString(querystring)
Dim sb As New StringBuilder("<br />")
For Each s As String In qscoll.AllKeys
Response.Write(s & " - " & qscoll(s) & "<br />")
Next s
var q = Request.QueryString;
NameValueCollection qscoll = HttpUtility.ParseQueryString(q.ToString());
I translate to C# version of josh-brown in VB
private System.Collections.Specialized.NameValueCollection ParseQueryString(Uri uri)
{
var result = new System.Collections.Specialized.NameValueCollection(4);
var query = uri.Query;
if (!String.IsNullOrEmpty(query))
{
var pairs = query.Substring(1).Split("&".ToCharArray());
foreach (var pair in pairs)
{
var parts = pair.Split("=".ToCharArray(), 2);
var name = System.Uri.UnescapeDataString(parts[0]);
var value = (parts.Length == 1) ? String.Empty : System.Uri.UnescapeDataString(parts[1]);
result.Add(name, value);
}
}
return result;
}
let search = window.location.search;
console.log(search);
let qString = search.substring(1);
while(qString.indexOf("+") !== -1)
qString = qString.replace("+", "");
let qArray = qString.split("&");
let values = [];
for(let i = 0; i < qArray.length; i++){
let pos = qArray[i].search("=");
let keyVal = qArray[i].substring(0, pos);
let dataVal = qArray[i].substring(pos + 1);
dataVal = decodeURIComponent(dataVal);
values[keyVal] = dataVal;
}
This is my code, I think it's very useful:
public String GetQueryString(string ItemToRemoveOrInsert = null, string InsertValue = null )
{
System.Collections.Specialized.NameValueCollection filtered = new System.Collections.Specialized.NameValueCollection(Request.QueryString);
if (ItemToRemoveOrInsert != null)
{
filtered.Remove(ItemToRemoveOrInsert);
if (!string.IsNullOrWhiteSpace(InsertValue))
{
filtered.Add(ItemToRemoveOrInsert, InsertValue);
}
}
string StrQr = string.Join("&", filtered.AllKeys.Select(key => key + "=" + filtered[key]).ToArray());
if (!string.IsNullOrWhiteSpace(StrQr)){
StrQr="?" + StrQr;
}
return StrQr;
}

How can I convert a JSON string to URL parameters (GET request)?

I have the following JSON which has to be converted to URL parameters for a GET request.
An example is given here, however due to the complexity of this object, there can be multiple line_items_attributes each with the given values as shown, I'm having difficulties passing on the correct one.
I've also tried to just serialize the JSON object and pass on that value but that did not solve the issue either.
{
"purchase_invoice":
{
"date":"14/04/2015",
"due_date":"14/04/2015",
"contact_id":500,
"contact_name":"TestContact",
"reference":"TestReference",
"line_items_attributes":[
{
"unit_price":10.00,
"quantity":1,
"description":"TestLineItemAttDesc",
"tax_code_id":1,
"ledger_account_id":501,
"tax_rate_percentage":19.0,
"tax_amount":1.60
}]
}
}
I've been searching for a while now but without much luck. Any insights are appreciated and most welcome!
This is calling an API which does not support the incoming data in JSON format, so doing this server-side or changing the web service to support data in JSON format is not possible.
x-www-form-urlencoded content is, essentially, a flat sequence of key/value tuples, and as explained in this answer to How do I use FormUrlEncodedContent for complex data types? by Tomalak, there is no canonical way to transform a hierarchical, nested key/value structure into a flat one.
Nevertheless, from the accepted answer to this question, this example from the Stripe API, and the question mentioned above, it seems that it is common to flatten parameters inside complex nested objects by surrounding their keys in brackets and appending them to the topmost key like so:
{
{ "purchase_invoice[date]", "14/04/2015" }
{ "purchase_invoice[due_date]", "14/04/2015" }
{ "purchase_invoice[contact_id]", "500" }
{ "purchase_invoice[contact_name]", "TestContact" }
{ "purchase_invoice[reference]", "TestReference" }
{ "purchase_invoice[line_items_attributes][0][unit_price]", "10" }
{ "purchase_invoice[line_items_attributes][0][quantity]", "1" }
{ "purchase_invoice[line_items_attributes][0][description]", "TestLineItemAttDesc" }
{ "purchase_invoice[line_items_attributes][0][tax_code_id]", "1" }
{ "purchase_invoice[line_items_attributes][0][ledger_account_id]", "501" }
{ "purchase_invoice[line_items_attributes][0][tax_rate_percentage]", "19" }
{ "purchase_invoice[line_items_attributes][0][tax_amount]", "1.6" }
}
If this is what you want, you can generate such key/value pairs with json.net using the following extension methods:
public static partial class JsonExtensions
{
public static string ToUrlEncodedQueryString(this JContainer container)
{
return container.ToQueryStringKeyValuePairs().ToUrlEncodedQueryString();
}
public static IEnumerable<KeyValuePair<string, string>> ToQueryStringKeyValuePairs(this JContainer container)
{
return container.Descendants()
.OfType<JValue>()
.Select(v => new KeyValuePair<string, string>(v.ToQueryStringParameterName(), (string)v));
}
public static string ToUrlEncodedQueryString(this IEnumerable<KeyValuePair<string, string>> pairs)
{
return string.Join("&", pairs.Select(p => HttpUtility.UrlEncode(p.Key) + "=" + HttpUtility.UrlEncode(p.Value)));
//The following works but it seems heavy to construct and await a task just to built a string:
//return new System.Net.Http.FormUrlEncodedContent(pairs).ReadAsStringAsync().Result;
//The following works and eliminates allocation of one intermediate string per pair, but requires more code:
//return pairs.Aggregate(new StringBuilder(), (sb, p) => (sb.Length > 0 ? sb.Append("&") : sb).Append(HttpUtility.UrlEncode(p.Key)).Append("=").Append(HttpUtility.UrlEncode(p.Value))).ToString();
//Answers from https://stackoverflow.com/questions/3865975/namevaluecollection-to-url-query that use HttpUtility.ParseQueryString() are wrong because that class doesn't correctly escape the keys names.
}
public static string ToQueryStringParameterName(this JToken token)
{
// Loosely modeled on JToken.Path
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JToken.cs#L184
// By https://github.com/JamesNK
if (token == null || token.Parent == null)
return string.Empty;
var positions = new List<string>();
for (JToken previous = null, current = token; current != null; previous = current, current = current.Parent)
{
switch (current)
{
case JProperty property:
positions.Add(property.Name);
break;
case JArray array:
case JConstructor constructor:
if (previous != null)
positions.Add(((IList<JToken>)current).IndexOf(previous).ToString(CultureInfo.InvariantCulture)); // Don't localize the indices!
break;
}
}
var sb = new StringBuilder();
for (var i = positions.Count - 1; i >= 0; i--)
{
var name = positions[i];
// TODO: decide what should happen if the name contains the characters `[` or `]`.
if (sb.Length == 0)
sb.Append(name);
else
sb.Append('[').Append(name).Append(']');
}
return sb.ToString();
}
}
Then if you have a JSON string, you can parse it into a LINQ-to-JSON JObject and generate the query string like so:
var obj = JObject.Parse(jsonString);
var queryString = obj.ToUrlEncodedQueryString();
Alternatively, if you have some hierarchical data model POCO, you can generate your JObject from the model using JObject.FromObject():
var obj = JObject.FromObject(myModel);
var queryString = obj.ToUrlEncodedQueryString();
Demo fiddle here.
So the final URL would be easy to compute using any URL Encoding mechanism. In C#, we could do the following:
string json = "...";
string baseUrl = "http://bla.com/somepage?myJson="
string urlWithJson = baseUrl + System.Net.WebUtility.UrlEncode(json)
Is there any way you can POST the data or otherwise send a request body instead? It would seem slightly easier/cleaner.
Sounds like you need something which is x-www-form-urlencoded.
From your example, it would look like this:
purchase_invoice%5Bdate%5D=14%2F04%2F2015&purchase_invoice%5Bdue_date%5D=14%2F04%2F2015&purchase_invoice%5Bcontact_id%5D=500&purchase_invoice%5Bcontact_name%5D=TestContact&purchase_invoice%5Breference%5D=TestReference&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bunit_price%5D=10&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bquantity%5D=1&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bdescription%5D=TestLineItemAttDesc&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_code_id%5D=1&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bledger_account_id%5D=501&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_rate_percentage%5D=19&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_amount%5D=1.6
The best reference for this encoding that I'm aware of is the undocumented jQuery.param method on the jQuery JavaScript library.

Serializing a list of dynamic objects to a CSV with ServiceStack.Text

All of my EF classes have a Projection() method that helps me choose what I want to project from the class to the SQL queries:
Example:
public static Expression<Func<Something, dynamic>> Projection()
{
return e => new
{
something = e.SomethingId,
name = e.Name,
requiredThingId = e.RequiredThingId,
requiredThing = new
{
requiredThingId = e.RequiredThing.RequiredThingId,
name = e.RequiredThing.Name,
...
},
optionalThingId = e.OptionalThingId,
optionalThing = e.OptionalThingId == null ? null : new
{
optionalThingId = e.OptionalThing.OptionalThingId,
name = e.OptionalThing.Name
}
...
};
}
I use these projection methods like this:
List<dynamic> projected = dbContext.Something.Select(Something.Projection()).ToList();
This way lets me reuse my projections all around my project.
I want to use ServiceStack.Text to serialize these lists to CSV.
I'm doing this:
string csvString = CsvSerializer.SerializeToCsv<dynamic>(Something.Select(Something.Projection()).ToList());
byte[] csvBytes = System.Text.Encoding.Unicode.GetBytes(csvString);
return File(csvBytes, "text/csv", "foo.csv");
But the result is an empty csv (csvString is full of "\r\n"'s and nothing more)
Questions:
Am I using correctly the SerializeToCsv() method?
Can I use <dynamic> in this library?
Suggestions for achieving my purpose?
For the example above the desired CSV would flatten all the properties, something like:
"somethingId";"name";"requiredThingId";"requiredThing.requiredThingId";"requiredThing.name";"optionalThingId";"optionalThing.optionalThingId";"optionalThing.name"
I will answer my own questions, but will not mark as accepted in hope of a new greater answer..
Am I using correctly the SerializeToCsv() method? Can I use dynamic
in this library?
Answer: Yes to both but ServiceStack.Text v4.0.36 is needed (which is available at MyGet, not Nuget)
Suggestions for achieving my purpose?
Answer: With ServiceStack.Text it is possible to serialize to CSV a List<dynamic>, but all the nested properties will be rendered as JSON and they will not be flattened out, eg:
List<dynamic> list = new List<dynamic>();
list.Add(new
{
name = "john",
pet = new
{
name = "doggy"
}
});
string csv = CsvSerializer.SerializeToCsv(list);
This list will be serialized to this csv:
name, pet
"john", { name = "doggy" }
And not to this csv, as I was expecting:
name, pet_name
"john", "doggy"
So... I finally ended up writing this code:
public class CsvHelper
{
public static string GetCSVString(List<dynamic> inputList)
{
var outputList = new List<Dictionary<string, object>>();
foreach (var item in inputList)
{
Dictionary<string, object> outputItem = new Dictionary<string, object>();
flatten(item, outputItem, "");
outputList.Add(outputItem);
}
List<string> headers = outputList.SelectMany(t => t.Keys).Distinct().ToList();
string csvString = ";" + string.Join(";", headers.ToArray()) + "\r\n";
foreach (var item in outputList)
{
foreach (string header in headers)
{
if (item.ContainsKey(header) && item[header] != null)
csvString = csvString + ";" + item[header].ToString();
else
csvString = csvString + ";";
}
csvString = csvString + "\r\n";
}
return csvString;
}
private static void flatten(dynamic item, Dictionary<string, object> outputItem, string prefix)
{
if (item == null)
return;
foreach (PropertyInfo propertyInfo in item.GetType().GetProperties())
{
if (!propertyInfo.PropertyType.Name.Contains("AnonymousType"))
outputItem.Add(prefix + "__" + propertyInfo.Name, propertyInfo.GetValue(item));
else
flatten(propertyInfo.GetValue(item), outputItem, (prefix.Equals("") ? propertyInfo.Name : prefix + "__" + propertyInfo.Name));
}
}
}
What this does is:
It flattens the List, so that all the properties of the objects in the list are primitives (eg: no nested properties)
It creates a CSV from that flattened list.
This algorithm is O(n*m), being
n: number of items in the list
m: number of properties inside each item (including nested properties)
Whilst the recommendation is to use clean POCO's for Serialization, you can serialize in-line anonymous types, e.g:
var somethings = dbContext.Something.Select(e => new {
something = e.SomethingId,
name = e.Name
});
somethings.ToCsv().Print();
Otherwise I've just added support for serializing IEnumerable<object> and IEnumerable<dynamic> in this commit.
This change is available from v4.0.36+ that's now available on MyGet.
I believe that part of what is going on is that the library is using properties in the object to determine what to serialize. I believe that a dynamic object does not construct properties like it's a POCO. If you don't setup a getter and setter on your object, it certainly won't work. Just for reference, I using version 4.5.6.0 of this library.

How to do a multiple case insensitive replace using a StringBuilder

I have a (large) template and want to replace multiple values. The replacement needs to be done case insensitive. It must also be possible to have keys that does not exist in the template.
For example:
[TestMethod]
public void ReplaceMultipleWithIgnoreCaseText()
{
const string template = "My name is #Name# and I like to read about #SUBJECT# on #website#, tag #subject#";
const string expected = "My name is Alex and I like to read about C# on stackoverflow.com, tag C#";
var replaceParameters = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("#name#","Alex"),
new KeyValuePair<string, string>("#subject#","C#"),
new KeyValuePair<string, string>("#website#","stackoverflow.com"),
// Note: The next key does not exist in template
new KeyValuePair<string, string>("#country#","The Netherlands"),
};
var actual = ReplaceMultiple(template, replaceParameters);
Assert.AreEqual(expected, actual);
}
public string ReplaceMultiple(
string template,
IEnumerable<KeyValuePair<string, string>> replaceParameters)
{
throw new NotImplementedException(
"Implementation needed for many parameters and long text.");
}
Note that if I have a list of 30 parameters and a large template, I do not want 30 large strings in memory. Using a StringBuilder seems to be an option, but other solutions are also welcome.
Solution I tried but did not work
Solution found here (C# String replace with dictionary) throws an exception when a key is not in the colletion, but our users makes mistakes and in that case I want to just leave the wromg key in the text. Example:
static readonly Regex re = new Regex(#"\$(\w+)\$", RegexOptions.Compiled);
static void Main2()
{
// "Name" is accidentally typed by a user as "nam".
string input = #"Dear $nam$, as of $date$ your balance is $amount$";
var args = new Dictionary<string, string>(
StringComparer.OrdinalIgnoreCase) {
{"name", "Mr Smith"},
{"date", "05 Aug 2009"},
{"amount", "GBP200"}};
// Works, but not case insensitive and
// uses a lot of memory when using a large template
// ReplaceWithDictionary many args
string output1 = input;
foreach (var arg in args)
{
output1 = output1.Replace("$" + arg.Key +"$", arg.Value);
}
// Throws a KeyNotFoundException + Only works when data is tokenized
string output2 = re.Replace(input, match => args[match.Groups[1].Value]);
}
Using a StringBuilder seems to be an option, but other solutions are also welcome.
Since you want case insensitive, I'd suggest (non StringBuilder):
public static string ReplaceMultiple(
string template,
IEnumerable<KeyValuePair<string, string>> replaceParameters)
{
var result = template;
foreach(var replace in replaceParameters)
{
var templateSplit = Regex.Split(result,
replace.Key,
RegexOptions.IgnoreCase);
result = string.Join(replace.Value, templateSplit);
}
return result;
}
DotNetFiddle Example
This is based off of Marc's answer the only real change is the check during the replacement and the boundary regex rule:
static readonly Regex re = new Regex(#"\b(\w+)\b", RegexOptions.Compiled);
static void Main(string[] args)
{
string input = #"Dear Name, as of dAte your balance is amounT!";
var replacements = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{"name", "Mr Smith"},
{"date", "05 Aug 2009"},
{"amount", "GBP200"}
};
string output = re.Replace(input, match => replacements.ContainsKey(match.Groups[1].Value) ? replacements[match.Groups[1].Value] : match.Groups[1].Value);
}
And here is a 5000 iterations test benchmark, have not looked at memory or anything else.
Replacement function is the one you have checked as the accepted answer.
I think I might have something you could try. I used something similar to it for email templates
public string replace()
{
string appPath = Request.PhysicalApplicationPath;
StreamReader sr = new StreamReader(appPath + "EmailTemplates/NewMember.txt");
string template = sr.ReadToEnd();
template = template.Replace("<%Client_Name%>",
first_name.Text + " " + middle_initial.Text + " " + last_name.Text);
//Add Customer data
template = template.Replace("<%Client_First_Name%>", first_name.Text);
template = template.Replace("<%Client_MI%>", middle_initial.Text);
template = template.Replace("<%Client_Last_Name%>", last_name.Text);
template = template.Replace("<%Client_DOB%>", dob.Text);
return template;
}
Inside of your template you can have tags such as <% %> as place holders for the values you want
Hope this helps!
The answer of Marc Gravell: C# String replace with dictionary can be changed an little bit so it does not throws an exception when the match can not be found. In this case it simply does not replace the match.
In case the string to be replace is tokenized, this is the solution:
static readonly Regex RegExInstance = new Regex(#"\$(\w+)\$", RegexOptions.Compiled);
public string ReplaceWithRegEx(string template, Dictionary<string, string> parameters)
{
return RegExInstance.Replace(template, match => GetNewValue(parameters, match));
}
private string GetNewValue(Dictionary<string, string> parameters, Match match)
{
var oldValue = match.Groups[1].Value;
string newValue;
var found = parameters.TryGetValue(oldValue, out newValue);
if (found)
{
return newValue;
}
var originalValue = match.Groups[0].Value;
return originalValue;
}
I have tested the solution with a 100.000 bytes string, 7 keys and hundreds of replacements. It uses 7 times more memory then the lenght of the string. And it took only 0.002 seconds.

Save two string array values into single table using foreach

In my view i am having two types of values and i am passing into string arrays. I can't concatenate both. I am using multiselect checkbox..
My code:
string[] func= { };
string[] role= { };
if (!string.IsNullOrEmpty(collection["Area"]))
func = collection["Area"].Split(',');
if (!string.IsNullOrEmpty(collection["Role"]))
roles= collection["Role"].Split(',');
foreach (string pf in func )
{
if (!string.IsNullOrEmpty(collection["role_" + func]))
role= collection["role_" + func].Split(',');
if (role!= null && role.Length > 0)
{
foreach (string rl in role)
{
prefunccpf = new prefunc();
cpf.CID= cid;
cpf.FID= Convert.ToInt32(func);
cpf.RID= Convert.ToInt32(rl);
}
}
}
In this first foreach statement i am checking functions array then it will come inside. Then Second foreach statement i am checking roles array. If roles array having count means it will save with roleId. But This conditions are goodly work if i select single function. If I select 2 and more function the loop again goes to top and again come to pref.roles. It didn't check the already those roles are saved. it is increasing again. Actually what will do for this?
I found the solution. By using Zip function in C# i got the exact result.
MY CODE HERE
string[] func= { };
string[] rol= { };
if (!string.IsNullOrEmpty(collection["Area"]))
func= collection["Area"].Split(',');
if (!string.IsNullOrEmpty(collection["Role"]))
roles= collection["Role"].Split(',');
var lstCombined = roles.Zip(func, (role, func) => new { Role = role, Function = func }).ToList();
foreach (var pf in lstCombined)
{
var cpf= new prefunc
{
CID= candidateId,
FID= Convert.ToInt32(pf .Function),
RID= Convert.ToInt32(pf .Role)
};
}

Categories