I'm working with a dictionary of type <string,string> like this:
Dictionary<string, string> parameters = new Dictionary<string, string>()
{
{"llave1", "valor1"},
{"llave2", "valor2"},
{"llave3", "valor3"},
{"llave4", "valor4"}
};
I want to get a string like this:
"llave1=valor1&llave2=valor2&llave3=valor3&llave4=valor4"
to solve this problem I made this:
foreach (var element in parameters)
{
strParameters += element.Key + "=" + element.Value;
if (index < parameters.Count)
{
strParameters += "&";
index++;
}
}
I wanted to know any way to get the same string result but using linq or String.Join I'm trying to refactory my code
Related
I have a HttpContext context and I iterate through some of its Request properties to collect info. Currently I do it this way:
if (context.Request.Headers?.Keys != null)
{
var items = new StringBuilder();
foreach (var key in context.Request.Headers.Keys)
{
items.AppendLine(key + " = " + context.Request.Headers[key]);
}
result.Headers = items.ToString();
}
if (context.Request.Form?.Keys != null)
{
var items = new StringBuilder();
foreach (var key in context.Request.Form.Keys)
{
items.AppendLine(key + " = " + context.Request.Form[key]);
}
result.Form = items.ToString();
}
if (context.Request.Query?.Keys != null)
{
var items = new StringBuilder();
foreach (var key in context.Request.Query.Keys)
{
items.AppendLine(key + " = " + context.Request.Query[key]);
}
result.Query = items.ToString();
}
I want to convert this repetitive code to a generic method (if you can suggest any other ways, I would be fine with them, too). I tried writing a generic method:
private static string ParseKeys<T>(IDictionary<object, object> dict)
{
var sb = new StringBuilder();
foreach (var key in dict.Keys)
{
sb.AppendLine(key + " = " + dict[key]);
}
return sb.ToString();
}
and calling it like this:
result.Headers = ParseKeys<IHeaderDictionary>(context.Request.Headers);
result.Form = ParseKeys<IFormCollection>(context.Request.Form);
result.Query = ParseKeys<IQueryCollection>(context.Request.Query);
But I get such errors: cannot convert from 'Microsoft.AspNetCore.Http.IHeaderDictionary' to 'System.Collections.Generic.IDictionary<object, object>'
I tried various combinations but I still couldn't manage to avoid errors. Am I trying to do impossible here, or is there a simple to do what I want?
Those collections you mention (IHeaderDictionary, IFormCollection and IQueryCollection) all implement the same interface: IEnumerable<KeyValuePair<string, StringValues>> so here you don't need a generic method. Instead, you can do something like this:
private static string ParseKeys(IEnumerable<KeyValuePair<string, StringValues>> values)
{
var sb = new StringBuilder();
foreach (var value in values)
{
sb.AppendLine(value.Key + " = " + string.Join(", ", value.Value));
}
return sb.ToString();
}
And call it as you were previously:
result.Headers = ParseKeys(context.Request.Headers);
result.Form = ParseKeys(context.Request.Form);
result.Query = ParseKeys(context.Request.Query);
You are using a generic method, but you are not using the generic type parameter T. As far as the IHeaderDictionary and the IFormCollection, it is enough to accept an IEnumerable<KeyValuePair<string, string[]>>, since both interfaces inherit it:
private static string ParseKeys(IEnumerable<KeyValuePair<string, string[]>> dict)
{
var sb = new StringBuilder();
foreach (var keyValuePair in dict)
{
sb.AppendLine(keyValuePair.Key + " = " + String.Join(", ", keyValuePair.Value));
}
return sb.ToString();
}
As far as the IQueryCollection is concerned, it is an IEnumerable<KeyValuePair<string, StringValues>>. You can easily transform this to the required type with an iterator:
private static IEnumerable<KeyValuePair<string, string[]>> Transform(IEnumerable<KeyValuePair<string, StringValues>> source) {
foreach(var item in source) {
yield return new KeyValuePair<string, string[]>(item.Key, item.Value.ToArray());
}
}
Eventually, you can call the method like:
result.Headers = ParseKeys(context.Request.Headers);
result.Form = ParseKeys(context.Request.Form);
result.Query = ParseKeys(Transform(context.Request.Query));
I am trying to customise a DevExpress grid filter.
Currently I return the data from my api, and so I need to parse the built in string which is returned from the grid.
An example of the filter string is;
StartsWith([name], 'test') And StartsWith([quantity], '12') And
StartsWith([id], '1') And StartsWith([date], '01/10/2015')
I would like to convert this to a Dictionary in the most efficient way?
You could use Regex for filtering the key/value pair outta your string and a simple foreach to prepare the dictionary.
This could be a solution:
public static Dictionary<string, object> FilterAPIData(string data)
{
var r = new Regex(#"\[\w+\], \'[\w/]+\'");
var result = r.Matches(data);
var dict = new Dictionary<string, object>();
foreach (Match item in result)
{
var val = item.Value.Split(',');
dict.Add(val[0], val[1]);
}
return dict;
}
Regex might be the best option for this, but I'll show you how to do it without Regex as it can be a bit difficult to understand.
Assuming your string will always be in this format you can do this:
string str = "StartsWith([name], 'test') And StartsWith([quantity], '12') And StartsWith([id], '1') And StartsWith([date], '01/10/2015')";
var strArray = str.Split(new string[]{"And "}, StringSplitOptions.None);
var dict = new Dictionary<string, string>();
foreach(var value in strArray)
{
dict.Add(GetStringBetween(value, "[", "]"), GetStringBetween(value, "'", "'"));
}
private string GetStringBetween(string value, string startDelim, string endDelim)
{
int first = value.IndexOf(startDelim) + startDelim.Length;
int last = value.LastIndexOf(endDelim);
return value.Substring(first, last - first);
}
//Output :
//name test
//quantity 12
//id 1
// date 01/10/2015
If there other formats the string can be in you can adjust as needed. I would also consider adding in more validation/error handling, but I will let you figure that out ;)
I have a string that looks like this:
TYPE Email Forwarding
SIGNATURE mysig.html
COMPANY Smith Incorp
CLIENT NAME James Henries
... heaps of others ....
I need to get the values of Type, Signature, Company and Client Name. There are others but once I can find a soution on how to do these, I can do the rest. I have tried to split and trim the string but then it splits fields like CLIENT NAME or on values like Email Forwarding.
I would put all of the "key" values into a collection, and then parse the string into another collection and then compare the values of the collections.
Here is a rough outline of how you could get the values:
static void Main(string[] args)
{
//Assuming that you know all of the keys before hand
List<string> keys = new List<string>() { "TYPE", "SIGNATURE", "COMPANY", "CLIENT NAME" };
//Not sure of the origin of your string to parse. You would have to change
//this to read a file or query the DB or whatever
string multilineString =
#"TYPE Email Forwarding
SIGNATURE mysig.html
COMPANY Smith Incorp
CLIENT NAME James Henries";
//Split the string by newlines.
var lines = multilineString.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
//Iterate over keys because you probably have less keys than data in the event of duplicates
foreach (var key in keys)
{
//Reduce list of lines to check based on ones that start with a given key
var filteredLines = lines.Where(l => l.Trim().StartsWith(key)).ToList();
foreach (var line in filteredLines)
{
Console.WriteLine(line.Trim().Remove(0, key.Length + 1));
}
}
Console.ReadLine();
}
That will do your job.
If it is multiple lines then you can loop through each line and call KeyValue extension method as given below:
public static class Program
{
public static void Main()
{
var value = "TYPE Email Forwarding".KeyValue();
var value1 = "CLIENT NAME James Henries".KeyValue();
}
public static KeyValuePair<string, string> KeyValue(this string rawData)
{
var splitValue = rawData.Split(new[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries);
KeyValuePair<string, string> returnValue;
var key = string.Empty;
var value = string.Empty;
foreach (var item in splitValue)
{
if (item.ToUpper() == item)
{
if (string.IsNullOrWhiteSpace(key))
{
key += item;
}
else
{
key += " " + item;
}
}
else
{
if (string.IsNullOrWhiteSpace(value))
{
value += item;
}
else
{
value += " " + item;
}
}
}
returnValue = new KeyValuePair<string, string>(key, value);
return returnValue;
}
}
Please note that this logic will work only when keys are all upper and the values are not all upper case. Otherwise, there is no way to identify which one is key (without having a manual track on keys) and which one is not.
I have this code:
NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (KeyValuePair<String,String> pr in nv) {
//process KeyValuePair
}
This compiles, but when I try to run it I get an InvalidCastException.
Why is this? Why can't I use KeyValuePair to iterate over a NameValueCollection, and what should I use instead?
First of all, NameValueCollection doesn't use KeyValuePair<String,String>. Also, foreach only exposes the key:
NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (string key in nv) {
var value = nv[key];
}
You can't do that directly, but you can create an extension method like so:
public static IEnumerable<KeyValuePair<string, string>> AsKVP(
this NameValueCollection source
)
{
return source.AllKeys.SelectMany(
source.GetValues,
(k, v) => new KeyValuePair<string, string>(k, v));
}
Then you can do:
NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (KeyValuePair<String,String> pr in nv.AsKVP()) {
//process KeyValuePair
}
Note: inspired by this. SelectMany is required to handle duplicate keys.
vb.net version:
<Extension>
Public Function AsKVP(
source As Specialized.NameValueCollection
) As IEnumerable(Of KeyValuePair(Of String, String))
Dim result = source.AllKeys.SelectMany(
AddressOf source.GetValues,
Function(k, v) New KeyValuePair(Of String, String)(k, v))
Return result
End Function
For future reference, you could also use this syntax:
foreach(string key in Request.QueryString)
{
var value = Request.QueryString[key];
}
ANother extension method, for learning purposes:
public static IEnumerable<KeyValuePair<string, string>> ToIEnumerable(this NameValueCollection nvc)
{
foreach (string key in nvc.AllKeys)
{
yield return new KeyValuePair<string, string>(key, nvc[key]);
}
}
NameValueCollection uses the old-skool enumerator:
var enu = ConfigurationManager.AppSettings.GetEnumerator();
while(enu.MoveNext())
{
string key = (string)enu.Current;
string value = ConfigurationManager.AppSettings[key];
}
I did like this and it works:
foreach (string akey in request.Query.Keys.Cast<string>())
writer.WriteLine(akey + " = " + request.Query[akey]);
Be aware that the key name might appear more than once in the query string and that the comparison is usually case sensitive.
If you want to just get the value of the first matching key and not bothered about case then use this:
public string GetQueryValue(string queryKey)
{
foreach (string key in QueryItems)
{
if(queryKey.Equals(key, StringComparison.OrdinalIgnoreCase))
return QueryItems.GetValues(key).First(); // There might be multiple keys of the same name, but just return the first match
}
return null;
}
To convert nameValueCollection from QueryString to List you can use extension like this:
public static List<KeyValuePair<string, string>> GetParams(this HttpRequest request)
{
var nameValueCollection = HttpUtility.ParseQueryString(request.QueryString.Value);
List<KeyValuePair<string, string>> keyValueCollections = new List<KeyValuePair<string, string>>();
if (!nameValueCollection.IsNullOrEmpty())
{
foreach (var key in nameValueCollection.AllKeys)
{
keyValueCollections.Add(new KeyValuePair<string, string>(key, nameValueCollection[key]));
}
}
return keyValueCollections;
}
public static void PrintKeysAndValues2( NameValueCollection myCol )
{
Console.WriteLine( " [INDEX] KEY VALUE" );
for ( int i = 0; i < myCol.Count; i++ )
Console.WriteLine( " [{0}] {1,-10} {2}", i, myCol.GetKey(i), myCol.Get(i) );
Console.WriteLine();
}
http://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection.aspx
public static string DictToQueryString(Dictionary<string, string> data)
{
string querystring = "";
foreach (string key, string val in data)
querystring += key + "=" + val + "&";
return querystring;
}
How i foreach?
Your "code" would have an extraneous "&" on the end. Do you want this? It's likely that you don't want this but please correct if you do. Assuming not, the simplest approach is to let String.Join do its job:
public static string DictToQueryString(Dictionary<string, string> data) {
return String.Join(
"&",
data.Select(kvp => String.Format("{0}={1}", kvp.Key, kvp.Value))
.ToArray()
);
In C# 4.0 the call to ToArray will be obviated.
Like this:
public static string DictToQueryString(Dictionary<string, string> data)
{
StringBuilder queryString = new StringBuilder();
foreach(var pair in data)
{
if (queryString.Length > 0)
queryString.AppendFormat("&{0}={1}", pair.Key, pair.Value);
else
queryString.AppendFormat("{0}={1}", pair.Key, pair.Value);
}
return queryString.ToString();
}
I believe this is what you're looking for?
public static string DictToQueryString(Dictionary<string, string> data)
{
string querystring = "";
foreach (string key in data.Keys)
{
string val = data[key];
querystring += key + "=" + val + "&";
}
return querystring;
}