How to count the number of unique values ​​in a dictionary?

I have a dictionary with doubles as values ​​and strings as keys.

I want to count the occurrences of each value in this dictionary, and I want to know this value (that is, it repeats, for example).

eg:

key1, 2 key2, 2 key3, 3 key4, 2 key5, 5 key6, 5 

I want to get a list:

 2 - 3 (times) 3 - 1 (once) 5 - 2 (twice) 

How can i do this?

+7
source share
2 answers

The first thing to note is that you really don't care about dictionary keys. Therefore, the first step is to ignore them as non-essential tasks. We will work with the Values property of the dictionary, and the work is the same as for any other set of integers (or, indeed, any other enumerated of any other type that we can compare for equality).

There are two general approaches to this problem that you should be aware of.

The first uses a different dictionary to store the values:

 //Start with setting up the dictionary you described. Dictionary<string, int> dict = new Dictionary<string, int>{ {"key1", 2}, {"key2", 2}, {"key3", 3}, {"key4", 2}, {"key5", 5}, {"key6", 5} }; //Create a different dictionary to store the counts. Dictionary<int, int> valCount = new Dictionary<int, int>(); //Iterate through the values, setting count to 1 or incrementing current count. foreach(int i in dict.Values) if(valCount.ContainsKey(i)) valCount[i]++; else valCount[i] = 1; //Finally some code to output this and prove it worked: foreach(KeyValuePair<int, int> kvp in valCount)//note - not sorted, that must be added if needed Console.WriteLine("{0} - {1}", kvp.Key, kvp.Value); 

Hope this is pretty simple. Another approach is more complicated, but has some advantages:

 //Start with setting up the dictionary you described. Dictionary<string, int> dict = new Dictionary<string, int>{ {"key1", 2}, {"key2", 2}, {"key3", 3}, {"key4", 2}, {"key5", 5}, {"key6", 5} }; IEnumerable<IGrouping<int, int>> grp = dict.Values.GroupBy(x => x); //Two options now. One is to use the results directly such as with the //equivalent code to output this and prove it worked: foreach(IGrouping<int, int> item in grp)//note - not sorted, that must be added if needed Console.WriteLine("{0} - {1}", item.Key, item.Count()); //Alternatively, we can put these results into another collection for later use: Dictionary<int, int> valCount = grp.ToDictionary(g => g.Key, g => g.Count()); //Finally some code to output this and prove it worked: foreach(KeyValuePair<int, int> kvp in valCount)//note - not sorted, that must be added if needed Console.WriteLine("{0} - {1}", kvp.Key, kvp.Value); 

(Most likely, we will use var rather than the detailed IEnumerable<IGrouping<int, int>> , but you need to be precise when writing code).

In direct comparison, this version is inferior - and more difficult to understand, and less effective. However, the study of this approach allows you to use several brief and effective options for the same methodology, so it’s worth considering.

GroupBy() performs an enumeration and creates another enumeration containing key-value pairs in which the value is also an enumeration. The lambda x => x means that it is grouped by itself, but we have the flexibility for different grouping rules than this. The contents of grp look something like this:

 { {Key=2, {2, 2, 2}} {Key=3, {3}} {Key=5, {5, 5}} } 

So, if we skip this for each group, we pull out Key and call Count() in the group, we get the desired results.

Now, in the first case, we created our account in one pass O (n), and here we create a group in the pass O (n), and then we get the counter in the second O (n) pass, making it much less efficient. It's also a little harder to understand, so why bother with this?

Well, firstly, when we understand this, we can rotate the lines:

 IEnumerable<IGrouping<int, int>> grp = dict.Values.GroupBy(x => x); foreach(IGrouping<int, int> item in grp) Console.WriteLine("{0} - {1}", item.Key, item.Count()); 

IN:

 foreach(var item in dict.Values.GroupBy(x => x)) Console.WriteLine("{0} - {1}", item.Key, item.Count()); 

It is rather brief and becomes idiomatic. This is especially nice if we want to continue and do something more complex with pairs of numbers-values, since we can relate this to another operation.

The version that puts the results in the dictionary can be even more concise:

 var valCount = dict.Values.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count()); 

There, your whole question was answered in one short line, not 6 (cutting comments) for the first version.

(Some may prefer to replace dict.Values.GroupBy(x => x) with dict.GroupBy(x => x.Value) , which will have exactly the same results as soon as we run Count() on it. If you don’t immediately sure why, try to work it out).

Another advantage is that in other cases we have great flexibility with GroupBy . For these reasons, people who are used to using GroupBy may well start with a single-line replica dict.Values.GroupBy(x => x).ToDictinary(g => g.Key, g => g.Count()); , and then move on to a more detailed, but more efficient format of the first version (where we increase the current results in the new dictionary) if it turned out to be a performance access point.

+9
source

Even simpler:

 Private Function CountOccurenceOfValue(dictionary As Dictionary(Of Integer, Integer), valueToFind As Integer) As Integer Return (From temp In dictionary Where temp.Value.Equals(valueToFind) Select temp).Count() End Function 

(Yes, this is in VB.NET, but you shouldn't have much trouble converting to C # :-))

-one
source

All Articles