converting a dictionary of dictionary to string - c#

i have a dictionary of dictionaries:
Dictionary<string, Dictionary<char, string>> myDict;
i'm trying to output its contents by overriding the to string method but i cant figure out how to convert it to string.
i tried:
StringBuilder str = new StringBuilder();
foreach (Dictionary<string, Dictionary<char, string>> s in str)
{
myDict.Append(s.Keys).Append(",").Append(s.Values);
}
this did not work

Your version tries printing s.Keys and s.Values, which are both collections. You can print them using a loop, but you could use string.Join twice instead:
public override string ToString() {
return string.Join(
",",
myDict.Select(
p => string.Format(
"{0}:{1}",
p.Key,
string.Join(",", p.Value.Select(x => string.Format("{0}={1}", x.Key, x.Value)))
)
);
}
Note 1: the above syntax uses string.Format, rather than string interpolation, in both calls. If you are using a newer version of C#, you could rewrite it for a somewhat shorter code, for example
string.Join(",", p.Value.Select(x => $"{x.Key}={x.Value}"))
Note 2: For older versions of .NET add ToArray() after both selects:
return string.Join(
",",
myDict.Select(
p => string.Format(
"{0}:{1}",
p.Key,
string.Join(",", p.Value.Select(x => string.Format("{0}={1}", x.Key, x.Value)).ToArray())
).ToArray()
));

Consider using Newtonsoft.Json, which supports serializing dictionaries to string.
var values = new Dictionary<string, IDictionary<char, string>>
{
{
"test", new Dictionary<char, string>
{
['a'] = "apple",
['b'] = "banana",
['c'] = "cat"
}
},
{
"lights", new Dictionary<char, string>
{
['c'] = "compact fluorescent",
['l'] = "light emitting diode",
['i'] = "incandescent"
}
}
};
Console.WriteLine(JsonConvert.SerializeObject(values));
Output:
{"test":{"a":"apple","b":"banana","c":"cat"},"lights":{"c":"compact fluorescent","l":"light emitting diode","i":"incandescent"}}

Try this one:
StringBuilder str = new StringBuilder();
foreach (var s in myDict)
{
foreach (var i in s.Value)
{
//Key is char, Value is string
str.Append("(")
.Append(s.Key)
.Append(",")
.Append(i.Key)
.Append(",")
.Append(i.Value)
.Append("),");
}
}
//remove trailing ","
var s = str.ToString().Trim(",");

Related

List<KeyValuePair> overrides added values

I am currently facing an issue where I want to add different values to the same Key in a foreach loop.
List<KeyValuePair<string, Dictionary<string, string>>> sysList = new List<KeyValuePair<string, Dictionary<string, string>>>();
Dictionary<string, string> newSystem = new Dictionary<string, string>();
string line1="";
string line2="";
string quit="";
foreach(Worksheet ws in workbook.Worksheets)
{
while(quit != q)
{
newSystem.Clear();
line1 = Console.ReadLine();
line2 = Console.ReadLine();
quit = Console.ReadLine();
}
newSystem.Add(line1, line2);
sysList.Add(new KeyValuePair<string, Dictionary<string, string>>(ws.Name,newSystem));
}
For the first iteration (within while) of the first Worksheet ws everything is fine. If the I choose to do >1 iterations within this Worksheet, there is a new entry added, but the Dictionary values are all the same, f.e.:
syList[0]: "worksheetName","test1","test2"
syList[1]: "worksheetName","test1","test2"
syList[2]: "worksheetName","test1","test2"
If there are several foreach iterations, the names stay the same, but the Dictionary Key and Values added by newSys are the same [AFTER the second foreach iteration]:
syList[0]: "worksheetName1","test1","test2"
syList[1]: "worksheetName1","test1","test2"
syList[2]: "worksheetName1","test1","test2"
syList[3]: "worksheetName2","test1","test2"
syList[4]: "worksheetName2","test1","test2"
Initially I tried using Dictionaries, but could not handle the same keys properly and did not find a proper solution except for using List.
I am very grateful for any help provided.
If there are additional details that you require, please, let me know.
Edit:
desired result (example):
#########: ws.Name, line1, line2
syList[0]: "worksheetName1","ABC","1"
syList[1]: "worksheetName1","DEF","2"
syList[2]: "worksheetName1","ABC","5"
syList[3]: "worksheetName2","ABD","4"
syList[4]: "worksheetName2","ZZZ","1"
In case you don't want to maintain any uniqueness in the keys and just want a flat list, you can use the C#7 tuple syntax to build your list.
List<string> sheetNames = new List<string>() { "worksheetName1", "worksheetName2" };
var sysList = new List<(string SheetName, string line1, string line2)>();
string line1 = string.Empty;
string line2 = string.Empty;
string quit = string.Empty;
foreach (var sheet in sheetNames)
{
while (quit != "E")
{
line1 = Console.ReadLine();
line2 = Console.ReadLine();
quit = Console.ReadLine();
sysList.Add((sheet, line1, line2));
}
quit = string.Empty;
}
Try code below :
List<List<string>> syList = new List<List<string>>() {
new List<string>() {"worksheetName1","test1","test2"},
new List<string>() {"worksheetName1","test1","test2"},
new List<string>() {"worksheetName1","test1","test2"},
new List<string>() {"worksheetName2","test1","test2"},
new List<string>() {"worksheetName2","test1","test2"}
};
Dictionary<string, Dictionary<string, List<string>>> dict = syList
.GroupBy(x => x.First(), y => y)
.ToDictionary(x => x.Key, y => y
.GroupBy(a => a.Skip(1).FirstOrDefault(), b => b.Last())
.ToDictionary(a => a.Key, b => b.ToList()));
//using normal looping
Dictionary<string, Dictionary<string, List<string>>> dict2 = new Dictionary<string, Dictionary<string, List<string>>>();
foreach (List<string> sy in syList)
{
if (dict2.ContainsKey(sy[0]))
{
Dictionary<string, List<string>> tempDict = dict2[sy[0]];
if (tempDict.ContainsKey(sy[1]))
{
tempDict[sy[1]].Add(sy[2]);
}
else
{
List<string> newList = new List<string>() { sy[2] };
tempDict.Add(sy[1], newList);
}
}
else
{
Dictionary<string, List<string>> newDict = new Dictionary<string, List<string>>();
newDict.Add(sy[1], new List<string> { sy[2] });
dict2.Add(sy[0], newDict);
}
}

C# .net regex extracting a string from within a string

I have a string output that looks like this:
ID = GC5c.U.feab4bc5-8-92e-c486eaddddf8
AESKEY1 = efbf5c9db259e345c205b0da27f6fb459D
AESKEY2 = ea85af9f1e5f42ff4fe8b9f07e7dcebc68
DESKEY1 = 6388a9e1a2fc8981189f0f412ae4e8
ID = JNPa.T.71664548-82-be2-a51aadd4a6f3
AESKEY1 = 37af9242c8879414e420f46903c16adebd3
AESKEY2 = 1259a1f6f6da03cb3984a117ca617d9ff73
DESKEY1 = 9547dc08db70cb95789f3a59e5c6adebd31
How can I iterate through the ID(s) of this list and obtain the values within the string?
Open the file, iterate through each line, and put it into a Dictionary<string, string>
Dictionary<string, string> data = new Dictionary<string, string>();
using(StreamReader reader = new StreamReader("C:/YourFilePath.txt"))
{
while (reader.Peek() >= 0)
{
string[] line = reader.ReadLine().Split('=');
data.Add(line[0].TrimEnd(), line[1].TrimStart());
}
}
UPDATE: Note that this solution is using Dictionary, and so it does not allow duplicate keys. If you need to duplicate keys, I would suggest changing this solution for a List<KeyValuePair<string, string>>. The code will get a little dirtier by the way:
List<KeyValuePair<string, string>> data = new List<KeyValuePair<string, string>>();
using(StreamReader reader = new StreamReader("C:/YourFilePath.txt"))
{
while (reader.Peek() >= 0)
{
string[] line = reader.ReadLine().Split('=');
data.Add(new KeyValuePair(line[0].TrimEnd(), line[1].TrimStart()));
}
}
I am not sure Regex is the best suited for this scenario, however, please find below a solution using it with a named group "id".
var regex = new Regex(#"id.*=\s*(?<id>.+)", RegexOptions.IgnoreCase);
var input = #"ID = Gs3c.H.feab4bc5-6c00-4ee8-9e2e-c486eaddddf8
AESKEY1 = efbf5c9db259e345c205b0da27f6fb459D
AESKEY2 = ea85af9f1e5f42ff4fe8b9f07e7dcebc68
DESKEY1 = 6388a9e1a2fc8981189f0f412ae4e8
ID = JNPa.T.71664548-82-be2-a51aadd4a6f3
AESKEY1 = 37af9242c8879414e420f46903c16adebd3
AESKEY2 = 1259a1f6f6da03cb3984a117ca617d9ff73
DESKEY1 = 9547dc08db70cb95789f3a59e5c6adebd31";
var ids = regex.Matches(input).Cast<Match>().Select(m => m.Groups["id"]);
Here is the Regex test: Regex test
NicoRiff's suggestion of using regular parsing into a Dictionary is the way to go. Here's the obligatory LINQ-based approach:
var data = File.ReadLines("C:/your/file/path.txt")
.Select(l => l.Split('='))
.ToDictionary(k => k[0], v => v[1]);
EDIT: As Nico pointed out, when the dictionary is going to contain duplicate keys, then a dictionary won't work. However, you can still use LINQ to automatically arrange the dictionary so that the ID field of your source text is the key instead of the field names. For example, you can use the following:
var data = File.ReadLines("C:/your/file/path.txt")
.Where(l => !String.IsNullOrEmpty(l))
.Select((l, i) => new { ID = i / 4, Value = l.Split('=') })
.GroupBy(x => x.ID)
.Select(g => new { ID = g.First().Value[1].Trim(), Values = g.ToDictionary(k => k.Value[0].Trim(), v => v.Value[1].Trim()) })
.ToDictionary(k => k.ID, v => v.Values);
Afterwards, you can iterate through data to get your fields:
foreach (var d in data.Values)
{
string id = d["ID"];
string aesKey1 = d["AESKEY1"];
string aesKey2 = d["AESKEY2"];
string desKey1 = d["DESKEY1"];
}
If you like to put the data into a dictionary like: Dicationary<string, Dictionary<string,string>>
Where the key of the main dictionary is the value of ID, and the internal dictionaries are the values that follows, then the following would do it:
string[] lines = input.Split('\n');
var myDict = new Dictionary<string, Dictionary<string,string>>();
var currentKey = "";
foreach (string[] keyVal in lines.Where(line=>!string.IsNullOrWhiteSpace(line))
.Select(line => line.Split('=')))
{
if (keyVal[0].StartsWith("ID"))
{
currentKey = keyVal[1].Trim();
myDict.Add(currentKey, new Dictionary<string, string>());
}
else
{
myDict[currentKey].Add(keyVal[0].Trim(), keyVal[1].Trim());
}
}
The Regex would look like so:
#"^ID\s+=\s+(.+)$"
You can test it on Regex101 and Rextester

String data to be converted to Dictionary

I have a String
string data = "[City, Delhi]&[State, DL]&[Country, IN]";
from which I want a dictionary.
The approach I thought was
Split on "&"
In the resulting Array, parse each element
2.1 Replace "[" and "]"
2.2 Insert into the Dictionary
I hate this approach because my string already has "[" and "]" and I should be able to add it directly to Dictionary.
This is a good use case for regular expressions.
var d = Regex.Matches(data, #"\[(?<k>[^,]+), (?<v>[^]]+)\]")
.OfType<Match>()
.ToDictionary(m => m.Groups["k"].Value, m => m.Groups["v"].Value);
The approach you describe is probably as good as it's going to get.
A naive implementation (without error handling) would be:
var pairs = data.Split('&');
var dict = new Dictionary<string, string>();
foreach (var pair in pairs)
{
var parts = pair.Split(',');
dict.Add(
parts[0].TrimStart('['),
parts[1].TrimStart().TrimEnd(']'));
}
Or, using a regular expression to obtain the keys and values:
string data = "[City, Delhi]&[State, DL]&[Country, IN]";
var pairs = data.Split('&');
var dict = new Dictionary<string, string>();
var regex = new System.Text.RegularExpressions.Regex(#"\[(?<key>.*), (?<value>.*)\]");
foreach (var pair in pairs)
{
var match = regex.Match(pair);
// TODO: Error if match.Success == false ?
dict.Add(match.Groups["key"].Value, match.Groups["value"].Value);
}
You can try this using linq
string data = "[City, Delhi]&[State, DL]&[Country, IN]";
string[] arr = data.Replace("[", "").Replace("]", "").Split('&');
var dict = arr.ToList().ToDictionary(x => x.Split(',')[0], x => x.Split(',')[1]);
Try this...
private static void Splitter()
{
string data = "[City, Delhi]&[State, DL]&[Country, IN]";
Dictionary<string,string> dOutput = new Dictionary<string,string>();
string[] sArr = data.Split('&');
var v = from p in sArr
select p.Replace("[", "").Replace("]", "").Split(',');
var v2 = (from p in v
select p).ToDictionary(item => item[0], item => item[1]);
Console.WriteLine(v2.Count());
}
v2 is a dictionary object...

How to Sort NameValue Collection and Store it in a string

Here I am storing two set of querystring parameters into two different namevalue collection. The querystring parameter order may vary so I just want to sort the order and then I need to store namevalue collection to a string.
Updated Code :
string url1 = #"http://www.somewebsitesampletest.com/dcs7o?data=142248494&dcp=smre&nparam=4567P&email=xxx.com";
string url2 = #"http://www.somewebsitesampletest.com/dcs7o?dcp=smre&data=142248494&email=xxx.com&nparam=4567P";
var NameValueCollection1 = HttpUtility.ParseQueryString(url1);
var NameValueCollection2 = HttpUtility.ParseQueryString(url2);
ExpectedResult:
After Sorting and converting to string the result should look like the below one
string query1 = "data=142248494&dcp=smre&email=xxx.com&nparam=4567P";
string query2 = "data=142248494&dcp=smre&email=xxx.com&nparam=4567P";
Here's a solution using Linq.
Basically it changes the NameValueCollection to an IEnumerable of the keys using Cast<T>, then the rest is fairly self explanatory.
public string GetSortedQueryString(string url)
{
var queryString = HttpUtility.ParseQueryString(url);
// Ignore null keys (caused by your ?& at the start of the query string
var orderedKeys = queryString.Cast<string>().Where(k => k != null).OrderBy(k => k);
return string.Join("&", orderedKeys.Select(k => string.Format("{0}={1}", k, queryString[k])));
}
Results for your URLs would be:
data=142248494&dcp=smre&email=xxx.com&nparam=4567P
data=142248494&dcp=smre&email=xxx.com&nparam=4567P
Email comes before nparam, unlike your expected solution (I'm assuming that was a mistake).
use LINQ with a Dictionary and a list of KeyValuePair :
string url1 = #"http://www.somewebsitesampletest.com/dcs7o?&data=142248494&dcp=smre&nparam=4567P&email=xxx.com";
string query1 ="";
Dictionary<String, String> paramDict = new Dictionary<string, string>();
var query = from match in urlString.Split('?').Where(m => m.Contains('='))
.SelectMany(pr => pr.Split('&'))
where match.Contains('=')
select new KeyValuePair<string, String>(
match.Split('=')[0],
match.Split('=')[1]);
query.ToList().ForEach(kvp => paramDict.Add(kvp.Key, kvp.Value));
var List<KeyValuePair<string, string>> paramList = paramDict.ToList();
paramList.Sort();
foreach (KeyValuePair<string, int> pair in list)
{
query1+=pair.Key+"="+pair.Value+"&";
}
query1=query1.TrimEnd('&');
I made this fiddle because I needed to sort querystring values in order to properly compare URIs: (H/T to Jacob's answer)
https://dotnetfiddle.net/eEhkNk
This preserves duplicate keys:
public static string[] QueryStringOmissions = new string[] { "b" };
public static NameValueCollection SortAndRemove(NameValueCollection collection)
{
var orderedKeys = collection.Cast<string>().Where(k => k != null).OrderBy(k => k);
var newCollection = HttpUtility.ParseQueryString(String.Empty);
foreach(var key in orderedKeys)
{
if (!QueryStringOmissions.Contains(key))
{
foreach(var val in collection.GetValues(key).Select(x => x).OrderBy(x => x).ToArray())
{
newCollection.Add(key, val);
}
}
}
return newCollection;
}

Query a Dictionary of Dictionaries?

Please can you advise me on how to query a Dictionary of Dictionaries, and/or a Dictionary of List?
private Dictionary<string, Dictionary<DateTime, double>> masterDict= new Dictionary<string, Dictionary<DateTime, double>>();
Private Dictionary<string, List<DateTime>> masterList= new Dictionary<string, List<DateTime>>();
I know if I do the following, I get a list of the dictionaries contained in masterDict, but I'm not sure how to get at the values of those dictionaries.
foreach (var kvp in masterDictMethod())
{
Console.WriteLine("Key = {0}, Value = {1}",
kvp.Key, kvp.Value);
}
Thanks for looking ;)
In you foreach kvp.Value is the inner dictionary of every masterDict entry i.e. Dictionary<DateTime, double>
So, just foreach also over kvp.Value and you will get the inner values.
e.g.
foreach (var kvp1 in masterDictMethod())
{
Console.WriteLine("Key = {0}, Inner Dict:", kvp1.Key);
foreach (var kvp2 in kvp1.Value)
{
Console.WriteLine("Date = {0}, Double = {1}", kvp2.Key, kvp2.Value);
}
}
Use masterDict.Values
This one is:
var masterDictionary = new Dictionary<string, Dictionary<DateTime, double>>();
var query =
from kvp1 in masterDictionary
from kvp2 in kvp1.Value
select new {TheString = kvp1.Key, TheDate = kvp2.Key, TheDouble = kvp2.Value };
foreach(var x in query)
{
Console.WriteLine("{0} {1} {2}", x.TheString, x.TheDate, x.TheDouble);
}
And then the other one is:
var masterList= new Dictionary<string, List<DateTime>>();
var query =
from kvp in masterList
from val in kvp.Value
select new {TheString = kvp.Key, TheDate = val);
foreach(var x in query)
{
Console.WriteLine("{0} {1}", x.TheString, x.TheDate);
}
foreach (var key in masterDict.Keys)
{
var nestedDict = masterDict[key];
}
You asked about lists, dictionaries and dictionaries containing other dictionaries.
I had a similar topic recently, where I wanted to have a queryable dictionary (i.e. an extension method which allows to pass a query expression as lambda parameter), that you can use like:
var result = myDictionary.QueryDictionary(w => myList.Any(a => a == w.Key));
The purpose of this code line is to check if any key of the dictionary is contained in myList.
So what I did is this, I wrote the following extension method:
// extension method using lambda parameters
public static Dictionary<string, T> QueryDictionary<T>(
this Dictionary<string, T> myDict,
Expression<Func<KeyValuePair<string,T>, bool>> fnLambda)
{
return myDict.AsQueryable().Where(fnLambda).ToDictionary(t => t.Key, t => t.Value);
}
It can be used for every dictionary which has keys of type string and items of every object type T.
Now you can easily write queries by passing a lambda expression, as in the following example:
var list1 = new List<string>() { "a", "b" };
var myDict = new Dictionary<string, object>();
myDict.Add("a", "123"); myDict.Add("b", "456"); myDict.Add("c", "789");
var result = myDict.QueryDictionary(w => list1.Any(a => a == w.Key));
The result will contain items a and b, because they are contained in list1.
You can also query a dictionary of dictionaries, here's a C# example for LinqPad, but it can be used as a console application as well (just comment out the .Dump() statements and replace them by Console.WriteLine(...) statements):
void Main()
{
// *** Set up some data structures to be used later ***
var list1 = new List<string>() { "a", "b", "d" }; // a list
var myDict = new Dictionary<string, object>(); // the dictionary
myDict.Add("a", "123"); myDict.Add("b", "456"); myDict.Add("c", "789");
var myDict2 = new Dictionary<string, object>(); // 2nd dictionary
myDict2.Add("a", "123"); myDict2.Add("b", "456"); myDict2.Add("c", "789");
myDict.Add("d", myDict2); // add 2nd to first dictionary
// *** 1. simple query on dictionary myDict ***
var q1 = myDict.QueryDictionary(w => list1.Any(a => a == w.Key));
q1.Dump();
// *** 2. query dictionary of dictionary (q3 contains result) ***
var q2 =
(Dictionary<string, object>)q1.QueryDictionary(w => w.Key.Equals("d")).First().Value;
var q3 = q2.QueryDictionary(w => w.Key.Equals("b"));
q3.Dump();
}
// *** Extension method 'QueryDictionary' used in code above ***
public static class Extensions
{
public static Dictionary<string, T> QueryDictionary<T>(
this Dictionary<string, T> myDict,
Expression<Func<KeyValuePair<string, T>, bool>> fnLambda)
{
return myDict.AsQueryable().Where(fnLambda).ToDictionary(t => t.Key, t => t.Value);
}
}
Since this solution is using Generics, you can pass any lambda expression as search parameter, so it is very flexible.

Categories

Resources