Can't store multiple items in IDictionary [duplicate] - c#

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

Related

C# - Retrieve data from Database and store in 2 dimension dictionary?

Hello I try to do something like PHP thing that is retrieve data from database and store in 2 dimension collection (Dictionary)
I'm not sure I write it correctly or not.
Let's say my database table and expected structure result look like this (See the screenshot)
Click to see screenshot
public ActionResult ShowBook()
{
var books = from v in db.Books
select v;
Dictionary<string, Dictionary<string, string>> test = new Dictionary<string, Dictionary<string, string>>();
foreach (var item in books)
{
test[item.Book_Type_ID][item.Author_ID] = item.Book_Name;
}
return .....
}
But I has this error
System.Collections.Generic.KeyNotFoundException: 'The given key was not present in the dictionary.'
How could I do?
The problem is you have to initialize each inner Dictionary<string, string> when assigning a new key to your outer dictionary. Typically this means checking if this key exists, and if not, creating the object:
foreach (var item in books)
{
if(!test.ContainsKey(item.Book_Type_ID))
{
test[item.Book_Type_ID] = new Dictionary<string, string>();
}
//now we are sure it exists, add it
test[item.Book_Type_ID][item.Author_ID] = item.Book_Name;
}
The dictionary is 2-dimensional. When you initialize it
Dictionary<string, Dictionary<string, string>> test = new Dictionary<string, Dictionary<string, string>>();
The first dimension is initialized, but not the second - i.e. test is an empty dictionary. So when you try to add a book title to a second-dimension dictionary, there isn't a dictionary for it to be added to yet. You need to check this condition first, and create an entry if it doesn't already exist:
var books = from v in db.Books select v;
Dictionary<string, Dictionary<string, string>> test = new Dictionary<string, Dictionary<string, string>>();
foreach (var item in books)
{
if (!test.ContainsKey(item.Book_Type_ID))
test[item.Book_Type_ID] = new Dictionary<string, string>();
test[item.Book_Type_ID][item.Author_ID] = item.Book_Name;
}

Get Values from dictionary present in List

I have a list like,
List<string> list = new List<string>();
list.Add("MEASUREMENT");
list.Add("TEST");
I have a dictionary like,
Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("BPGA", "TEST");
dict.Add("PPPP", "TEST");
dict.Add("RM_1000", "MEASUREMENT");
dict.Add("RM_2000", "MEASUREMENT");
dict.Add("CDMA", "TEST");
dict.Add("X100", "XXX");
Now, I want to get all matched data from dictionary based on list.
Means, all data from list match with dict value then get new dictionary with following mathched values
Is there any way to achieve this by using lambda expression?
I want result like this.
Key Value
"BPGA", "TEST"
"PPPP", "TEST"
"RM_1000", "MEASUREMENT"
"RM_2000", "MEASUREMENT"
"CDMA", "TEST"
Thanks in advance!
You should be using the dictionary like it is intended to be used i.e. a common key with multiple values for example:
Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();
Then all you need to do when adding the values is:
dict.Add("TEST", new List<string>() { /*strings go in here*/ });
Then to get all the results from a key like:
List<string> testValues = dict["TEST"];
To make it safe however you should check that the key exists i.e.
if (dict.ContainsKey("TEST"))
{
//Get the values
}
Then to add values to a current key you go do something like:
dict["TEST"].Add("NewValue");
If you insist on keeping the same structure, although I do not recommend it, something like the following will work:
List<string> testKeys = new List<string>();
foreach (var pairs in dict)
{
if (pair.Value == "TEST")
{
testKeys.Add(pair.Key);
}
}
Or even the following LINQ statement:
List<string> testKeys = dict.Where(p => p.Value == "TEST").Select(p => p.Key).ToList();
For a generic query to find the ones from your list use:
List<string> values = dict.Where(p => list.Contains(p.Value)).ToList();

Checking all entries from one Dictionary are in another Dictionary

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

Alternate of if and foreach for Linq

Could there be a sophisticated LINQ for the following code.
My code is trying to prepare a dictionary of string(key), string(value), by first getting a list of objects from another dictionary and then looping over to that list of objects.
Dictionary<string, string> displayNames = new Dictionary<string, string>();
List<DefDefaultDataSet.dbEnumsRow> enumList;
//allEnums dictionary: Key as string and value as List<DefDefaultDataSet.dbEnumsRow>
//enumID is a string object
if (allEnums.TryGetValue(enumID, out enumList))
{
foreach (DefDefaultDataSet.dbEnumsRow row in enumList)
{
string enumValue = row.Value;
//If already have enumvalue ,no need to add again
if (!string.IsNullOrWhiteSpace(enumValue) && !displayNames.ContainsKey(enumValue))
{
displayNames.Add(enumValue, FindResourceVal(row.ResourceKey, uniqueKey));
}
}
}
You can keep the if statement, then filters using Where and create the dictionary using ToDictinary:
if (allEnums.TryGetValue(enumID, out enumList))
displayNames = enumList.Where(e => !string.IsNullOrWhiteSpace(e.Value) && !displayNames.ContainsKey(e.Value))
.ToDictionary(k => k.ResourceKey, v => FindResourceVal(v.ResourceKey, uniqueKey));

Linq strange behviour with dictionaries

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.

Categories

Resources