I have a Dictionary with doubles as values and strings as keys.
I want to count occurrences of each value in this Dictionary and I want to know this value (that is for instance repeated).
for instance:
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 it?
The first thing to note, is that you don't actually care about the keys of the dictionary. Step one therefore is to ignore them as irrelevant to the task in hand. We're going to work with the Values property of the dictionary, and the work is much the same as for any other collection of integers (or indeed any other enumerable of any other type we can compare for equality).
There are two common approaches to this problem, both of which are well worth knowing.
The first uses another dictionary to hold the count of 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);
Hopefully this is pretty straightforward. Another approach is more complicated but has some pluses:
//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);
(We'd probably use var rather than the verbose IEnumerable<IGrouping<int, int>>, but it's worth being precise when explaining code).
In a straight comparison, this version is inferior - both more complicated to understand and less efficient. However, learning this approach allows for some concise and efficient variants of the same technique, so it's worth examining.
GroupBy() takes an enumeration and creates another enumeration that contains key-value pairs where the value is an enumeration too. The lambda x => x means that what it is grouped by is itself, but we've the flexibilty for different grouping rules than that. The contents of grp looks a bit like:
{
{Key=2, {2, 2, 2}}
{Key=3, {3}}
{Key=5, {5, 5}}
}
So, if we loop through this an for each group we pull out the Key and call Count() on the group, we get the results we want.
Now, in the first case we built up our count in a single O(n) pass, while here we build up the group in a O(n) pass, and then obtain the count in a second O(n) pass, making it much less efficient. It's also a bit harder to understand, so why bother mentioning it?
Well, the first is that once we do understand it we can turn 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());
Into:
foreach(var item in dict.Values.GroupBy(x => x))
Console.WriteLine("{0} - {1}", item.Key, item.Count());
Which is quite concise, and becomes idiomatic. It's especially nice if we want to then go on and do something more complicated with the value-count pairs as we can chain this into another operation.
The version that puts the results into a dictionary can be even more concise still:
var valCount = dict.Values.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
There, your whole question answered in one short line, rather than the 6 (cutting out comments) for the first version.
(Some might prefer to replace dict.Values.GroupBy(x => x) with dict.GroupBy(x => x.Value) which will have exactly the same results once we run the Count() on it. If you aren't immediately sure why, try to work it out).
The other advantage, is that we have more flexibility with GroupBy in other cases. For these reasons, people who are used to using GroupBy are quite likely to start off with the one-line concision of dict.Values.GroupBy(x => x).ToDictinary(g => g.Key, g => g.Count()); and then change to the more verbose but more effient form of the first version (where we increment running totals in the new dictionary) if it proved a performance hotspot.
Even simpler would be:
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 it's in VB.NET, but you shouldn't have much trouble to convert to C# :-) )
Related
I have a dictionary in C#.
Dictionary originalMap = new Dictionary<string, List<CustomObject>>();
originalMap contents:
"abc" -> {CustomObject1, CustomeObject2, ..CustomObject100};
"def" -> {CustomObject101,CustomObject102, CustomObject103};
Now, finally I want to make sure that the count of all custome objects above does not exceed a limit - say 200.
While doing this, I want to make sure that I take top 200 ranked object (sorted by Score porperty).
I tried below but it doesn't work. It returns same number of objects.
var modifiedMap = new Dictionary<string, IList<CustomObject>>(CustomObject);
modifiedMap = originalMap.OrderByDescending(map => map.Value.OrderByDescending(customObject => customObject.Score).ToList().Take(200))
.ToDictionary(pair => pair.Key, pair => pair.Value);
Any help will be appreciated.
Thank you.
You're only performing the limit part while doing the ordering - not when you actually create the new dictionary.
It sounds like you want something like:
var modifiedMap = originalMap.ToDictionary(
pair => pair.Key,
pair => pair.Value.OrderByDescending(co => co.Score).Take(200).ToList());
Note that ordering the dictionary entries themselves would be pointless, as Dictionary<TKey, TValue> is inherently not an ordered collection.
For my project i made a dictionary that has a random double and a string that belongs to that double:
Dictionary<double, string> myDict = new Dictionary<double, string>();
For this project i know that the double is a random value, and within the dictionaries all strings are unique, with the exception that about 80% one of them is twice in the dictionary.
So what i want to do, is find the 2 strings that are a pair (the same string) and find the 2 double values that belong to these 2 string.
Basically my idea of doing this is by using IEnumerator counter = myDict.GetEnumerator(); and use the while (counter.MoveNext() == true) to start another IEnumerator that loops again through all the entries of the dictionary and compares by string, so if will find the pairs this way.
So for each entry in the dictionary, it will loop through the whole dictionary again to find pairs.
Now i get the feeling this might not be the best solution to handle this. Are there alternatives to find the pairs in the dictionary, or is this looping through the only real way of doing this?
I believe, you are looking to get Keys for those items where there is a pair of string available in Values.
var result = myDict.GroupBy(r => r.Value)
.Where(grp => grp.Count() == 2)
.SelectMany(grp => grp.Select(subItem => subItem.Key))
.ToList();
If you want to get keys for those items which have multiple string values, (more than two) then modify the condition to:
.Where(grp => grp.Count() >= 2)
Another thing to add, you are adding keys as Random values in the dictionary. Remember, Random doesn't mean Unique. You could end up with an exception since Dictionary keys are unique.
If your dictionary is defined as:
Dictionary<double, string> myDict = new Dictionary<double, string>
{
{1, "ABC"},
{2, "ABC"},
{3,"DEF"},
{4,"DEF"},
{5,"DEF2"},
{6,"XYZ"}
};
For output after the LINQ expression:
foreach (var d in result)
{
Console.WriteLine(d);
}
Output:
1
2
3
4
I'm new to lambdas and they seemed fairly straight-forward until I tried to do something more complex.
I have this dictionary.
Dictionary<int, int> dict = new Dictionary<int,int>();
of which I want to obtain the key of the key-val pair with the largest value. What I tried is:
dict.Keys.Max(g => dict[g])
The reasoning being that out of the list of Keys, pick that one for which dict[key] is largest. However, this picks the largest value itself, rather than its corresponding key.
dict.Keys.OrderByDescending(g => dict[g]).First() will accomplish what you want, but may be inefficient for large dictionaries. MaxBy in John Skeet's MoreLinq will do exactly what you want efficiently.
var maxValue = dict.Max((maxPair) => maxPair.Value);
var maxPairs = dict.Where((pair) => pair.Value == maxValue);
This will give you a list of all of the pairs that have the maximum value.
If you just want the keys, you can do this afterwards:
var maxKeys = maxPairs.Select((pair) => pair.Key);
I decided to add an answer based on my thoughts on McKay's. This will perform very fast given the standard LINQ methods, provides just the key:
var maxValue = dict.Max(p => p.Value);
var keys = dict.Where(p => p.Value == maxValue).Select(p => p.Key);
Now, if the OP knows that there is always just one key (no duplicate values) then an improvement (very small) would be to use First with this as due to lazy evaluation only the elements up to the one with the maximum value would be evaluated after all were evaluated to first find the maximum value:
var key = dict.Where(p => p.Value == maxValue).First().Key;
dict.OrderBy(v => v.Value).Last().Key;
should do it. Basically you are ordering the KeyValuePair by value and picking the last one which will be the maximum. And within the last one you are only interested in the Key.
It's hard to summarize this as a 1 line question. I have a class like this:
class Item
{
int Count {get; set;}
double Value {get; set;}
}
and a List<Item> that contains an arbitary number of Item values.
How can I get the Item with the lowest Count and the highest Value?
Performance is not important but elegance is, as I don't want to have huge nested loops to do this operation unless there is not elegant way, i.e. Linq, etc.
EDIT:
Here is a sample list that could have these Items:
{Count, Value}
{2, 10}, {6, 60}, {5, 21}, {4, 65}, {2, 35}, {4, 18}, {3, 55}, {7, 99}, {2, 25}
So here the value I want is {2, 35} because it has the lowest Count of all items, and for the items with the same Count values, it has the highest Value.
Okay, now we've got a bit of clarity:
as long as it has the lowest Count, the highest Value in that range is OK
It's easy...
var best = list.OrderBy(x => x.Count)
.ThenByDescending(x => x.Value)
.FirstOrDefault();
That will give null if the list is empty, but otherwise the item with the lowest count, and the one with the highest value if there are multiple with that count.
This is less efficient than it might be, as we could really do it in a single pass by creating a comparer to compare any two values - but this will certainly be the approach which gets the right element in the least code.
Hopefully it works now, sadly not so elegant like Jon's
var res = (from i in items
orderby i.Count ascending, i.Value descending
select i).FirstOrDefault();
With morelinq (which has MinBy and MaxBy operators), you can accomplish this easily in linear time (unfortunately needs an extra pass):
IEnumerable<item> items = ...
var minCount = items.Min(item => item.Count);
var minCountItemWithMaxValue = items.Where(item => item.Count == minCount)
.MaxBy(item => item.Value);
With an appropriate AllMinBy extension that returned all the minimum items in a sequence (missing in morelinq sadly), you could make this even more efficient:
var minCountItemWithMaxValue = items.AllMinBy(item => item.Count)
.MaxBy(item => item.Value);
EDIT:
Here's an (ugly) way to do it in O(1) space and O(n) time in a single pass with standard LINQ:
var minCountItemWithMaxValue = items.Aggregate(
(bestSoFar, next) =>
(next.Count < bestSoFar.Count) ? next :
(next.Count > bestSoFar.Count) ? bestSoFar :
(next.Value > bestSoFar.Value) ? next : bestSoFar);
I'm having a low-brainwave day... Does anyone know of a quick & elegant way to transform a Dictionary so that the key becomes the value and vice-versa?
Example:
var originalDictionary = new Dictionary<int, string>() {
{1, "One"}, {2, "Two"}, {3, "Three"}
};
becomes
var newDictionary = new Dictionary<string, int>();
// contents:
// {
// {"One", 1}, {"Two", 2}, {"Three", 3}
// };
Use ToDictionary ?
orignalDictionary.ToDictionary(kp => kp.Value, kp => kp.Key);
This works because IDictionary<TKey,TElement>; is also an IEnumerable<KeyValuePair<TKey,TElement>>;. Just be aware that if you have duplicate values, you will get an exception.
In case you have duplicate values, you will need to decide on what to do with them. One simple way would be to ignore duplicates by grouping on Value first, then make the dictionary.
originalDictionary
.ToLookup(kp => kp.Value)
.ToDictionary(g => g.Key, g => g.First().Key);
Here you are:
var reversed = orignalDictionary.ToDictionary(el => el.Value, el => el.Key);
I agree with the answers provided, however you should consider and make the change in your program to actually set up with the <key, value> instead of making this change after.
Is there a particular context in the application where you have 1-to-1 relation or is it global? If the latter, you may want to check out a BiDirectional Dictionary.