Reputation: 4090
I need to select a number of values (into a List) from a Dictionary based on a subset of keys.
I'm trying to do this in a single line of code using Linq but what I have found so far seems quite long and clumsy. What would be the shortest (cleanest) way to do this?
This is what I have now (the keys are Strings and keysToSelect is a List of keys to select):
List<ValueType> selectedValues = dictionary1.Where(x => keysToSelect.Contains(x.Key))
.ToDictionary<String, valueType>(x => x.Key,
x => x.Value)
.Values.ToList;
Thank you.
Upvotes: 48
Views: 62977
Reputation: 8500
Another option that doesn't do work twice:
var dict = new Dictionary<int, string> {
[1] = "one",
[2] = "two",
[3] = "three",
[4] = "four"
};
var keys = new[] { 2, 4, 1 };
var result = dict
.Join(keys, x => x.Key, k => k, (x, _) => x.Value)
.ToArray();
Upvotes: 0
Reputation: 60902
The accepted answer is not very efficient when keys are not guaranteed to exist b/c it performs two lookups.
Based on the accepted answer I came up with this extension method:
public static IEnumerable<TValue> GetMultiple<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, IEnumerable<TKey> keysToSelect)
{
foreach (var key in keysToSelect)
if (dictionary.TryGetValue(key, out TValue value))
yield return value;
}
Not exactly a "one liner" but I find it more readable than chaining four Linq methods.
Usage:
var results = dictionary.GetMultiple(keysToSelect).ToList()
Upvotes: 5
Reputation: 842
The accepted solution is still not the most efficient option from the point of lookups as you still have to check if the key is in the dictionary twice: once to filter the keys, once to lookup the object.
This solution does not have that issue:
var selectedValues = keysToSelect
.Select(_ => {
var found = dict.TryGetValue(_, out TValue? result);
return (found, result);
})
.Where(_ => _.found)
.Select(_ => _.result!)
.ToList();
Upvotes: 1
Reputation: 11866
Well you could start from the list instead of the dictionary:
var selectedValues = keysToSelect.Where(dictionary1.ContainsKey)
.Select(x => dictionary1[x])
.ToList();
If all the keys are guaranteed to be in the dictionary you can leave out the first Where
:
var selectedValues = keysToSelect.Select(x => dictionary1[x]).ToList();
Note this solution is faster than iterating the dictionary, especially if the list of keys to select is small compared to the size of the dictionary, because Dictionary.ContainsKey
is much faster than List.Contains
.
Upvotes: 77
Reputation: 700830
If you know that all the value that you want to select are in the dictionary, you can loop through the keys instead of looping through the dictionary:
List<ValueType> selectedValues = keysToSelect.Select(k => dictionary1[k]).ToList();
Upvotes: 3
Reputation: 26792
A Dictionary<TKey,TValue>
is IEnumerable<KeyValuePair<TKey,TValue>>
, so you can simply Select
the Value
property:
List<ValueType> selectedValues = dictionary1
.Where(x => keysToSelect.Contains(x.Key))
.Select(x => x.Value)
.ToList();
or
var selectValues = (from keyValuePair in dictionary1
where keysToSelect.Contains(keyValuePair.Key)
select keyValuePair.Value).ToList()
Upvotes: 9