LINQ filter by type on list from dictionary value
Think this is a very basic question, but it's my first LINQ query and I'm completely stuck:
I have a dictionary with string key and list value (see definition below) and want to pull out elements of a list of a particular type having selected the list by the dictionary key.
IDictionary<string, IList<MyBaseType>> dataItemMap;
Where MySubType extends MyBaseType.
My dodgy query is:
string identCode = "foo";
IEnumerable<MySubType> query =
from entry in dataItemMap
where entry.Key == identCode
select entry.Value.OfType<MySubType>();
And the error message (from LinqPad):
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable<S开发者_如何学编程ystem.Collections.Generic.IEnumerable<MySubType>>'
to 'System.Collections.Generic.IEnumerable<MySubType>'.
An explicit conversion exists (are you missing a cast?)
The problem is clearly in the entry.Value.OfType<> but how can I specify the lists elements? I'm looking for something like entry.Value.Items.OfType<> ?
thanks.
I think you want something like this:
IEnumberable<MySubType> query = dataItemMap[identCode].OfType<MySubType>();
This will get the list with the given key, and then filter it to return only MySubType elements.
EDIT: I've been focusing on why the existing solution didn't work (and the general problem of "I've got a list of values for each element, and I want to flatten it") rather than taking a step back. As Andy's answer shows, you should almost certainly use the fact that it's a dictionary - turning it from an O(n) operation to O(1) :)
Two caveats:
- Your current code will always perform an ordinal, culture-insensitive comparison with
identCode
and the dictionary keys; using the dictionary lookup will use whatever comparer it was constructed with. - Your current code will return an empty sequence if
identCode
isn't found in the dictionary; the dictionary indexer will throw an exception. You can useTryGetValue
if you want to avoid that.
Note that if you know that all the elements in the last you're picking are actually of the right type, it would probably be better to use Cast
than OfType
:
var query = dataItemMap[identCode].Cast<MySubType>();
I generally prefer Cast
to OfType
when both would work, as it means that if my assumptions about the data in the sequence prove incorrect, I find out about it with an exception rather than silently missing data.
Note that Cast
will also return null
elements, whereas OfType
won't.
No, the problem isn't in using OfType<>
- it's that you've ended up with a sequence of sequences, but you're trying to assign that to a single sequence.
Either change the return type, or use another from
clause to flatten the results:
IEnumerable<MySubType> query = from entry in dataItemMap
where entry.Key == identCode
from value in entry.Value.OfType<MySubType>()
select value;
I'd be tempted to use the extension methods directly:
var query = dataItemMap.Where(e => e.Key == identCode)
.SelectMany(e => e.Value.OfType<MySubType>());
精彩评论