Query a Dictionary of Dictionaries? - c#

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.

Related

C#: Intersect a dictionary with a list. Return dictionary item after match

how can I safety return the dictionary item which matches with the item from the list?
static void Test()
{
var dict = new Dictionary<string, string>();
dict.Add("license1", "123");
dict.Add("license2", "456");
dict.Add("license3", "789");
var list = new List<string>();
list.Add("444");
list.Add("111");
list.Add("123");
var result = dict.Values.Intersect(list);
//result should be only the matching item as a dictionary for dict -> for this example = "license1, 123"
}
Because the dictionary isn't arranged helpfully I think I might do:
var h = list.ToHashSet();
var result = dict.Where(kvp => h.Contains(kvp.Value));

Dynamic Linq Select from List<IDictionary<string, object>>

Consider I have this IQueryble database collection which is similar to this list List and linq:
var lstData = new List<IDictionary<string, object>>()
{
new Dictionary<string, object>() {
{ "Name" , "John"},
{ "Age", 20 },
{ "School", "Waterloo" }
},
new Dictionary<string, object>() {
{ "Name" , "Goli"},
{ "Age", 23 },
{ "School", "Mazandaran" }
},
};
var result = lstData.Select(x => new { Name= x["Name"], School= x["School"] });
However, I do not know the name of properties at compilation. How do I dynamically select specific columns in the Linq select at runtime? something like this:
var result= lstData .Select("Name,School");
It would be more efficient and more maintainable if you define a class that you could store your data for each person rather than storing it in an object which in that case it would need unnecessary boxing and unboxing every time you search through your list. having that said you could search in your data structure like this:
var result = lstData.Find(x => x["Name"] == (object)"John" && x["School"] == (object)"Waterloo");
var age = (int)result["Age"]; //20
Edit
If the goal is to get properties as an anonymous list without knowing the property names at compilation time it's not possible. the anonymous type in C# needs the property name first hand or at least property access when you define it. an alternative way to solve this problem would be to use another dictionary so you could map the original one into:
var summayList = lstData.Select(x => new Dictionary<string, object>()
{
{ "Name", x["Name"] },
{ "School", x["School"] }
});
var john = summayList.Single(x => x["Name"] == (object)"John");
if the number of properties is unknown in addition to their names, you could use a method that return a list of dictionaries given unknown number of property names:
public static IEnumerable<IDictionary<string, object>> GetProps(List<IDictionary<string, object>> list, params string[] props)
{
return list.Select(x => new Dictionary<string, object>(
props.Select(p => new KeyValuePair<string, object>(p, x[p]))));
}
Usage
var result = GetProps(lstData, "Name", "School");
var goli = result.Single(x => x["Name"] == (object)"Goli");
I have finished the code, try the following code.
Func<IDictionary<string, object>, dynamic> CreateDynamicFromDict(string fields)
{
// input parameter "x"
var xParameter = Expression.Parameter(typeof(IDictionary<string, object>), "x");
// output object
var result = Expression.Parameter(typeof(IDictionary<string, object>), "result");
var add = typeof(IDictionary<string, object>).GetMethod("Add");
var body = new List<Expression>();
//initial output object
body.Add(Expression.Assign(result, Expression.New(typeof(ExpandoObject))));
// set value "FieldN = x.FieldN"
var bindings = fields.Split(',').Select(o => o.Trim())
.Select(o =>
{
var key = Expression.Constant(o);
return Expression.Call(result, add, key, Expression.Property(xParameter, "Item", key));
});
body.AddRange(bindings);
// return value
body.Add(result);
var block = Expression.Block(new[] { result }, body);
var lambda = Expression.Lambda<Func<IDictionary<string, object>, dynamic>>(block, xParameter);
// compile to Func<IDictionary<string, object>, dynamic>
return lambda.Compile();
}
The usage is
var result = lstData.Select(CreateDynamicFromDict("Field1, Field2"));
References:
LINQ : Dynamic select
Expression tree create dictionary with property values for class
Expression Trees (C#)
Expression Tree

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);
}
}

How can I get datatable into Dictionary class?

In a windows form application (c#) I've a DataTable. This solution has a class called "AddressStandarizationSolution". What I'm trying to do is load my dictionary from my DataTable that is in the main form.
How could I do that?
public class AddressStandardizationSolution
{
public Dictionary<string, string> directionals = new Dictionary<string, string>();
public Dictionary<string, string> streetName= new Dictionary<string, string>();
public AddressStandardizationSolution()
{
var _with1 = directionals;
_with1.Add("E", "E");
_with1.Add("EAST", "E");
_with1.Add("E-R", "EAST");
_with1.Add("N", "N");
_with1.Add("NO", "N");
var _with2 = streetName;
//This DataTable has 100.000 records
_with1.Add(errorFromDataTable, rightFromDataTable);
}
}
I'm sorry. My DataTable fields are:
error | right
MNTGoMRY AVEnue | MONTGOMERY AVE
You could use Linq to do it. For example:
Dictionary<string, string> resultDictionary = dataTable.Rows.OfType<System.Data.DataRow>()
.Select( s =>
new
{
Error = s["Error"] as string,
Right = s["Right"] as string
}
).ToDictionary( k => k.Error, v => v.Right );
Just an alternative, although it can be combined with Mike Peterson's answer :)
table.AsEnumerable()
.Select(x => new KeyValuePair<string, string>(x.Field<string>("Error"), x.Field<string>("Right")))
.ToDictionary(x => x.Key, x => x.Value);

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;
}

Categories

Resources