I have come across through a strange behaviour of Linq : with two linq expressions that might seem identical I have different results! If I loop once I get the same result, but above it finds nothing.
Here is the code:
Dictionary<String, String> mainDico = new Dictionary<String, String>();
mainDico.Add("key1", "value1");
mainDico.Add("key2", "value2");
List<Dictionary<String, String>> ls = new List<Dictionary<String, String>>();
Dictionary<String, String> fistDico = new Dictionary<String, String>();
fistDico.Add("key1", "value1");
fistDico.Add("key2", "value2");
Dictionary<String, String> secondDico = new Dictionary<String, String>();
secondDico.Add("key1", "other");
secondDico.Add("key2", "other");
ls.Add(fistDico);
ls.Add(secondDico);
IEnumerable<Dictionary<String, String>> failQuery = from p in ls
select p;
IEnumerable<Dictionary<String, String>> successfulQuery = from p in ls
select p;
String[] items = new String[] { "key1","key2" }; // with one element it works
foreach (var item in items)
{
String temp = mainDico[item];
failQuery = failQuery.Where(p => p[item] == temp);
successfulQuery = successfulQuery.Where(p => p[item] == mainDico[item]);
}
Console.WriteLine(successfulQuery.SingleOrDefault() != null);//True
Console.WriteLine(failQuery.SingleOrDefault() != null);//False
The problem is that you're closing over the loop variable.
The problematic section of code is right here:
foreach (var item in items)
{
String temp = mainDico[item];
failQuery = failQuery.Where(p => p[item] == temp);
successfulQuery = successfulQuery.Where(p => p[item] == mainDico[item]);
}
You're creating a lambda that closes over item in the second case (and also the first case; you should really fix that), and you're not evaluating the query until after the end of the foreach loop; that means that item will always be the last item in the foreach loop, not the current item. This can be easily resolved by creating a new local variable, which is what you do in the first case, which is why that works.
Here is a related link that discuss the matter in more detail. (You can find lots more by searching over "close over loop variable".
Note that this was changed in C# 5.0 since it's a frequent cause of confusion and bugs. (This is likely why certain people couldn't reproduce the problem.)
It's also worth noting that this has nothing to do with the dictionary. In your query item is effectively always the last item in the foreach, rather than the current, which is why it fails. Anything that you did with item that relied on it being the current value wouldn't do what you wanted.
Related
The syntax for iterating over a dictionary with a foreach loop is:
foreach (KeyValuePair<key, value> item in dictionary)
Inside the foreach loop the key is accessed with item.key and the value with item.value.
This got me thinking, can this be used without the use of a foreach loop as a convenient (although niche) way to represent a specific dictionary pair?
I am not looking for some weird work arounds, like running a foreach loop and saving the KeyValuePair into a variable once the target key is reached, because at this point it would be more convenient to just use 2 variables.
Like this
var dic = new Dictionary<string, int>();
dic["a"] = 42;
KeyValuePair<string, int> keyVal;
foreach(var kv in dic) {
keyVal = kv; << gets the last entry from the dictioanry
}
Note that the dictionary does not store KeyValuePairs, it creates one for the enumeration, so the simple thing to do is this (because we are not expensively recreating something)
var dic = new Dictionary<string, int>();
dic["a"] = 42;
KeyValuePair<string, int> keyVal = new KeyValuePair<string, int>("a", dic["a"]);
this is more efficient than the (neat) LINQ Sinlge method
The IDictionary<TKey, TValue> interface implements IEnumerable<KeyValuePair<TKey,TValue>>. This means you can simply use Single() to get the entry you want.
IDictionary<string, int> dict = ...;
KeyValuePair<string, int> entry = dict.Single(it => it.Key == "yourKey");
try this
var dict = new Dictionary<string, string>() {
{"hi","Hello World!"},
{"adieu","Goodby"}
};
string hi = dict["hi"]; //Hello World!
or if you want a list
List<KeyValuePair<string,string>> list = dict.ToList();
result
[{"Key":"hi","Value":"Hello World!"},{"Key":"adieu","Value":"Goodby"}]
This question already has an answer here:
How to store multiple items in IDictionary?
(1 answer)
Closed 6 years ago.
I have created this IDictionary:
IDictionary<string, string> trace = new Dictionary<string, string>();
my goal is use it for save the content of json deserialized. I save the content in the IDictionary like this:
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
foreach (var item in obj)
{
trace["date"] = item.trace.details.date;
trace["type"] = item.trace.details.type;
}
now in the obj variable I have 180 elements, the foreach over all items available in obj. The problem is that in the trace dictionary for each iteration each item is replaced, so I get only the item of the last iteration. How can I save all items in the dictionary? A dictionary shouldn't push each item automatically in the next iteration, instead of replacing it?
As #Santosh pointed out, this is expected behavior. You could instead use a List<Dictionary<String,String>>
var traces = new List<Dictionary<string, string>>();
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
foreach (var item in obj)
{
var trace = new Dictionary<String,String>();
trace["date"] = item.trace.details.date;
trace["type"] = item.trace.details.type;
...
traces.Add(trace);
}
Try:
IDictionary<string, string> trace = new Dictionary<string, IList<string>>();
trace.Add("date", new List<string>())
trace.Add("type", new List<string>())
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
foreach (var item in obj)
{
trace["date"].Add(item.trace.details.date)
trace["type"].Add(item.trace.details.type)
}
Plz, feel free to improve this design.
This is not how dictionaries work in C#. As the name suggests it should be one key with one value. You don't have repeated entries in dictionaries, right?
What you're probably trying to do is add to values in each entry, so I'll suggest using Tuple, since I don't know the type of your json parsed data, I'll assume string for everything, but type really won't change anything here :
var list = new List<Tuple<string,string>>();
foreach (var item in obj)
{
list.Add(new Tuple<string, string>(item.trace.details.date, item.trace.details.type));
}
Now you'll reach each entry as list[i].Item1 for a date on a given i index and list[i].Item2 for a type on the same index.
Using LINQ, you can do this:
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
var traces = obj.Select(item => new Dictionary<string, string> {
{ "date", item.trace.details.date },
{ "type", item.trace.details.type }
});
You will get an IEnumerable<Dictionary<string, string>>. There isn't really a simpler way to do what you ask other than to use a collection of dictionaries.
Perhaps you can use a Lookup<string, string>, this is a one-to-many dictionary. You can create one using ToLookup() extension method:
var dates = obj.Select(o => new { Key = "date", Value = o.trace.details.date });
var types = obj.Select(o => new { Key = "type", Value = o.trace.details.type });
var lookUp = dates.Concat(types).ToLookup(kv => kv.Key, kv => kv.Value);
i have two Dictionarys A & B, i want to see if all entries in A exist in B. In the past i've compared Lists using the following:
var set1 = new HashSet<String>(list1);
var set2 = new HashSet<String>(list2);
return set1.SetEquals(set2);
What i have thought to do is simply loop over each value in Dictionary A using:
dictA.TryGetValue(dictBvalue, out item)
this will return null on the item var if the value isn't there, but this seems a little long winded.
Is there a quick and effcient way of comparing dictionaries?
Thanks.
You could use All extension and do this.
var allexist = list1.All(x=> list2.ContainsKey(x.Key) && list2[x.Key] == x.Value)
here is the solution if you want to loop over each value
Dictionary<string, string> dictA = new Dictionary<string, string>();
Dictionary<string, string> dictB = new Dictionary<string, string>();
bool allexist = true;
foreach (var itemA in dictA)
{
if (!dictB.ContainsKey(itemA.Key))
{
allexist = false;
}
}
Actually, you asked for a method comparing dictionaries but your code example refer to HashSet which is different.
For HashSets, you can use IsSubsetOf and SetEquals methods.
To compare dictionaries, you can use DictionaryEquals method from this answer
I thought it would have been relatively straightforward, I am here first runing this code that uses a 3rd party software to return a set of values as a dictionary:
List<Dictionary<string, long>> result = 3rdPartyConnection.GetPlans(id, params);
I then try to loop these results (so I can populate a viewmodel with them and other values) using the following code, first I declare a new list using the viewmodel, then I loop through the results to populate the model:
List<OptionViewModel> AvailableOptions = new List<OptionViewModel>();
foreach (KeyValuePair<string, long> item in result)
{
OptionViewModel c = new OptionViewModel();
c.Code = item.Key;
c.Value = item.Value;
AvailableOptions.Add(c);
}
But it only generates the following error:
Error 257 Cannot convert type 'System.Collections.Generic.Dictionary<string,long>' to 'System.Collections.Generic.KeyValuePair<string,long>'
I don't understand what the problem is, from what I've read on c# this is how you loop through a dictionary. I have a feeling this is a simple oversight of some kind on my part....
It is a List<> of dictionaries, not a single Dictionary<,>. Use two foreach loops inside each other.
Sometimes it is better to use var in foreach loops: foreach (var item in ...) It helps you figure out what the iteration variable type is, without introducing a cast.
You will need to iterate through the List first, going through each dictionary, at which point you can then iterate through the key-value pairs:
foreach (Dictionary<string, long> dic in result)
{
foreach(KeyValuePair<string, long> item in dic)
{
OptionViewModel c = new OptionViewModel();
c.Code = item.Key;
c.Value = item.Value;
AvailableOptions.Add(c);
}
}
Right now I have this complex function, I have a list of MainObjects(call them MO) and for each of these objects I have to loop over a list of objects(call them C) with a title, a status and a list of sub-objects (call them E). The function loops over these sub-objects(E) and uses it's title and quantity properties.
The goal of the function is to create a dictionary(D1), where the Key is a C(title) and the Values is another dictionary(D2), where the Key is E(title) and the Values yet another dictionary(D3), where the Key is C(status) and the value E(quantity).
So in the end I will have all (unique) C(title)'s wherein I can see all (unique) E(title)'s wherein I can see all different C(status)'s and the E(quantity) of these statuses (with as extra challenge if 2 E(quantity)'s would have the same status with the same title on the same course they should be added to each other and then put in as value).
I made this all work fine.
However. The function is big and hard to understand, so I'm looking for a more approachable way of dealing with this problem.
One of these ways was supposed to be LINQ. However, I have little to no knowledge about this and for a massively complex function as this I can hardly understand how to deal with this in LINQ.
I'm also concerned about performance since this WPF project is heavily dependable on user-experience. So I'm not sure if LINQ would actually make things faster, slower or same.
Here is where you guys come in.
Is LINQ a better way to deal with this problem?
Is the performance similar to the one of my function?
Is the LINQ query more understandable?
Is there an alternative way of dealing with this complex function
rather then the 2 methods I'm describing?
Underneath you will find the function I used to deal with this function my way.
It is done in 3 steps:
Step1: Loop the MO's, C's, E's and create a list of dictionaries.
Step2: Join the duplicate key's of the result of step1 and create a
first stage dictionary.
Step3: Split the deeper dictionaries so that
we can use the E object as intended.
Result: has been put in the 'final' object. A list of dictionaries with as keys C(title) and values a list of dictionaries. This list of dictionaries with as keys E(title) and values a Dictionary. This Dictionary has as keys C(status) and values E(quantity). This E(quantity) is a combined value of each quantity of each E of the same C(status) for a same C.
//DateTime start = DateTime.Now; //start performance test
//start -> step 1
List<Dictionary<string/*C(title)*/, Dictionary<int/*C(status)*/, List<E>>>> firstResultList = new List<Dictionary<string, Dictionary<int, List<E>>>>();
foreach(MO mo in listOfMOs)
{
foreach (C c in mo.listOfCs)
{
Dictionary<string, Dictionary<int, List<E>>> D1 = new Dictionary<string, Dictionary<int, List<E>>>();
int cStatus = c.status;
Dictionary<int, List<E>> D2 = new Dictionary<int, List<E>>();
List<E> eList = new List<E>();
foreach (E e in c.listOfEs)
{
eList.Add(e);
}
D2.Add(cStatus, eList);
D1.Add(c.Title, D2);
firstResultList.Add(D1);
}
}
//firstResultList = step1 results
//Console.WriteLine(firstResultList.ToString());
//
//step1 -> step2
Dictionary<string/*C(title)*/, List<Dictionary<int/*C(status)*/, List<E>>>> groupedDict = new Dictionary<string, List<Dictionary<int, List<E>>>>();
foreach (Dictionary<string, Dictionary<int, List<E>>> dict in firstResultList)
{
List<Dictionary<int, List<E>>> listje;
if(groupedDict.ContainsKey(dict.Keys.ElementAt(0)))
{
listje = groupedDict[dict.Keys.ElementAt(0)];
}
else
{
listje = new List<Dictionary<int, List<E>>>();
}
listje.Add(dict[dict.Keys.ElementAt(0)]);
groupedDict[dict.Keys.ElementAt(0)] = listje;
}
//groupedDict = step2 results
//Console.WriteLine(groupedDict.ToString());
//
//step2 -> step3
Dictionary<string/*C(title)*/, List<Dictionary<string/*E(title)*/, Dictionary<int/*C(status)*/, int/*E(quantity)*/>>>> final = new Dictionary<string, List<Dictionary<string, Dictionary<int, int>>>>();
int index = 0;
foreach (List<Dictionary<int, List<E>>> list in groupedDict.Values)
{
//Within one unique C
List<Dictionary<string, Dictionary<int, int>>> eStatusQuantityList = new List<Dictionary<string, Dictionary<int, int>>>();
foreach (Dictionary<int, List<E>> dict in list)
{
foreach (List<E> eList in dict.Values)
{
foreach(E e in eList)
{
if (eStatusQuantityList.Count > 0)
{
foreach (Dictionary<string, Dictionary<int, int>> dict2 in eStatusQuantityList)
{
Dictionary<int, int> statusQuantityDict;
if (dict2.ContainsKey(e.Title))
{
statusQuantityDict = dict2[e.Title];
//int quantity = statusQuantityDict.value//statusQuantityDict[dict.Keys.ElementAt(0)];
int quantity = 0;
int value;
bool hasValue = statusQuantityDict.TryGetValue(dict.Keys.ElementAt(0), out value);
if (hasValue) {
quantity = value;
} else {
// do something when the value is not there
}
statusQuantityDict[dict.Keys.ElementAt(0)] = quantity + e.Quantity;
dict2[e.Title] = statusQuantityDict;
}
else
{
statusQuantityDict = new Dictionary<int, int>();
statusQuantityDict.Add(dict.Keys.ElementAt(0), e.Quantity);
dict2.Add(e.Title, statusQuantityDict);
}
}
}
else
{
Dictionary<string, Dictionary<int, int>> test = new Dictionary<string, Dictionary<int, int>>();
Dictionary<int, int> test2 = new Dictionary<int, int>();
test2.Add(dict.Keys.ElementAt(0), e.Quantity);
test.Add(e.Title, test2);
eStatusQuantityList.Add(test);
}
}
}
}
//ending
string key = groupedDict.Keys.ElementAt(index);
final[key] = eStatusQuantityList;
index++;
//
}
//final contains step3 results
//Console.WriteLine(final.ToString());
/*
for (int i = 0; i<final.Keys.Count; i++)
{
Console.WriteLine(final.Keys.ElementAt(i));
}
for (int i = 0; i < final.Values.Count; i++)
{
Console.WriteLine(final.Values.ElementAt(i));
}
*/
//
//TimeSpan duration = DateTime.Now - start; //end performance test
//Console.WriteLine("That took " + duration.TotalMilliseconds + " ms"); //performance test results //60.006 is fine, 600.006 is OOM. //Our range of objects is max. 300 MO's though
As you can see this is a hell of a function. But it works fine (2-5ms (avg. 2.5) for our max target of MO's). But I can see people (other then myself) messing up when they have to readjust this function for some reason. So any improvement in maintainability or readability would be cool.
Is LINQ a better way to deal with this problem?
Better is subjective. Better looking? Better performance? Better (as in easier) understanding?
Is the performance similar to the one of my function?
LINQ performance is usually not quite as good as doing it manually, however there is always a trade off because LINQ can be (not always) easier to understand.
Is the LINQ query more understandable?
It can be. But if you've ever used reSharper where it looks at your code and says it can turn it into a LINQ query then you'll know that sometimes it makes it less understandable.
Is there an alternative way of dealing with this complex function
rather then the 2 methods I'm describing?
Mix-n-match? You can hand-code performance critical parts and leave the rest in LINQ. But to find the performance critical parts you should use a profiler rather than just guessing.