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
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.
I have a dictionary of lists.
var dicAclWithCommonDsEffectivity = new Dictionary<string, List<int>>();
var list1 = new List<int>(){1,2,3};
var list2 = new List<int>(){2,4,6};
var list3 = new List<int>(){3,7,6};
var list4 = new List<int>(){8,7,6};
dicAclWithCommonDsEffectivity.Add("ab",list1);
dicAclWithCommonDsEffectivity.Add("bc",list2);
dicAclWithCommonDsEffectivity.Add("cd",list3);
dicAclWithCommonDsEffectivity.Add("de",list4);
I want to get the keys in dictionary for which atleast one matching value with the current key list.
for key "ab"(first list). I should get: "ab","bc" and "cd".Since these lists contain one of the matching element in {1,2,3}
Is there a way without looping through each item in the list of dictionary value.
Is there a way without looping through each item in the list of dictionary value.
Something has to loop - dictionaries are only designed to look up by key, and you're not doing that other than for the first check.
You can do this fairly easily though:
private IEnumerable<string> GetMatchingKeys(
Dictionary<string, List<int>> dictionary, string key)
{
// TODO: Use TryGetValue if key might not be in dictionary
HashSet<int> elements = new HashSet<int>(dictionary[key]);
return dictionary.Where(pair => pair.Value.Any(x => elements.Contains(x)))
.Select(pair => pair.Key);
}
This uses the fact that Dictionary implements IEnumerable<KeyValuePair<TKey, TValue>> - so the Where clause checks a particular entry by spotting if any of the elements of its value matches any of the elements of the original value. The Select clause then projects the pair to just the key.
If you need to do this a lot and you're concerned about efficiency, another alternative would be to build a second dictionary from int to List<string> - basically a reverse mapping. You'd need to maintain that, but then you could easily fetch all the "original keys" mapping to each of the values corresponding to the given key, and just use Distinct to avoid duplicates.
It's always better to check for existence of the searchKey in the Dictionary before accessing them. Then you can get the associated list in the dictionary for that particular key.
You can also try like this:
string searchKey = "ab";
if (dicAclWithCommonDsEffectivity.ContainsKey(searchKey))
{
var ListToSearch = dicAclWithCommonDsEffectivity[searchKey];
var resultKeys = dicAclWithCommonDsEffectivity.Where(x =>
x.Value.Any(y => ListToSearch.Contains(y)))
.Select(x => x.Key)
.ToList();
}
else
{
// Specified key was not found
}
If what you mean is to visibly (not logically) remove the looping, you could use LINQ with proper Where filter to do that and Select the keys from the Dictionary which have any value element(s) intersect(s) with the selected List (List in the Dictionary with key == "ab") like this:
string key = "ab";
List<int> selectedList = dicAclWithCommonDsEffectivity[key];
var results = dicAclWithCommonDsEffectivity
.Where(x => x.Value.Any(y => selectedList.Contains(y)))
.Select(x => x.Key);
If you want to logically remove the looping too, please consider Mr. Skeet's answer.
I'm trying to remove all elements in a IDictionary object that match a condition.
E.g. the IDictionary contains a set of keys and corresponding values (let say 80 objects). The keys are strings, the values could be of different types (think extracting metadata from a wtv file using directshow).
Some of the keys contains the text "thumb", e.g. thumbsize, startthumbdate etc.
I want to remove all objects from the IDictionary who's keys contain the word thumb.
The only way I'm seeing here is to manually specify each key name using the .Remove method.
Is there some way to get all the objects who's keys contain the word thumb and them remove them from the IDictionary object.
The code looks like this:
IDictionary sourceAttrs = editor.GetAttributes();
GetAttributes is defined as:
public abstract IDictionary GetAttributes();
I don't have control over GetAttributes, it's returns an IDictionary object, I only know the contents by looking at it while debugging. (likely a HashTable)
UPDATE: Final Answer thanks to Tim:
sourceAttrs = sourceAttrs.Keys.Cast<string>()
.Where(key => key.IndexOf("thumb", StringComparison.CurrentCultureIgnoreCase) == -1)
.ToDictionary(key => key, key => sourceAttrs[key]);
So you want to remove all entries where the key contains a sub-string.
You can use LINQ by keeping all that does not contain it:
dict = dict
.Where(kv => !kv.Key.Contains("thumb"))
.ToDictionary(kv => kv.Key, kv => kv.Value);
If you want a case-insensitive comparison you can use IndexOf:
dict = dict
.Where(kv => kv.Key.IndexOf("thumb", StringComparison.CurrentCultureIgnoreCase) == -1)
.ToDictionary(kv => kv.Key, kv => kv.Value);
Update according to your non-generic edit:
If it's a non-generic dictionary like a HashTable you cannot use LINQ directly, but if you know that the key is a string you could use following query:
// sample IDictionary with an old Hashtable
System.Collections.IDictionary sourceAttrs = new System.Collections.Hashtable
{
{"athumB", "foo1"},
{"other", "foo2"}
};
Dictionary<string, object> newGenericDict = sourceAttrs.Keys.Cast<string>()
.Where(key => !key.Contains("thumb"))
.ToDictionary(key => key, key => sourceAttrs[key]);
But maybe it's actually a generic Dictionary, you can try-cast with the as operator:
var dict = sourceAttrs as Dictionary<string, object>;
It's null if the cast didn't work.
If your Dictionary is read only, you will need to remove the items one by one, in which case you can also use LINQ:
dict
.Keys
.Where(p => p.Contains("thumb"))
.ToList
.ForEach(p => dict.Remove(p);
Note this works because at removal you are not looping through the dictionary anymore: you first loop entirely through the dictionary to build a list of keys to delete, then you loop through this list and re-access the dictionary to remove the keys one by one.
If your dictionary is not read only and efficiency is a concern, you are better off with Tim's suggestions.
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# :-) )
I have a dictionary with non unique values and I want to count the matches of a string versus the values.
Basically I now do dict.ContainsValue(a) to get a bool telling me if the string a exists in dict, but I want to know not only if it exists but how many times it exists (and maybee even get a list of the keys it exists bound to)
Is there a way to do this using dictionary, or should I look for a different collection?
/Rickard Haake
To get the number of instances of the value you could do something like this:
dict.Values.Count(v => v == a);
To find the keys that have this value you could do this:
dict.Where(kv => kv.Value == a).Select(kv => kv.Key);
To get the count use Values.Count:
int count = dict.Values.Count(x => x == "foo");
To get the keys I prefer the query syntax:
var keys = from kvp in dict
where kvp.Value == "foo"
select kvp.Key;
Note that this will require scanning the entire dictionary. For small dictionaries or infrequent lookups this may not be a problem.
If you are making many lookups you may wish to maintain a second dictionary that maps the values to the keys. Whilst this will speed up lookups, it will slow down modifications as both dictionaries will need updating for each change.
what about using LINQ: if a is the value you're looking for, the the code could be
dict.Values.Where(v => v == a).Count();