I've been having trouble articulating the differences between ILookup<TKey, TVal> and IGrouping<TKey, TVal>, and am curious if I understand it correctly now. LINQ compounded the issue by producing sequences of IGrouping items while also giving me a ToLookup extension method. So it felt like they were the same until I looked more closely.
var q1 =
from n in N
group n by n.MyKey into g
select g;
// q1 is IEnumerable<IGrouping<TKey, TVal>>
Which is equivalent to:
var q2 = N.GroupBy(n => n.MyKey, n => n);
// q2 is IEnumerable<IGrouping<TKey, TVal>>
Which looks a lot like:
var q3 = N.ToLookup(n => n.MyKey, n => n);
// q3 is ILookup<TKey, TVal>
Am I correct in the following analogies?
- An
IGrouping<TKey, TVal>is a single group (i.e. a keyed sequence), analogous toKeyValuePair<TKey, TVal>where the value is actually a sequence of elements (rather than a single element) - An
IEnumerable<IGrouping<TKey, TVal>>is a sequence of those (similar to what you get when iterating over anIDictionary<TKey, TVal> - An
ILookup<TKey, TVal>is more like aIDictionary<TKey, TVal>where the value is actually a sequence of elements
Yes, all of those are correct.
And
ILookup<TKey, TValue>also extendsIEnumerable<IGrouping<TKey, TValue>>so you can iterate over all the key/collection pairs as well as (or instead of) just looking up particular keys.I basically think of
ILookup<TKey,TValue>as being likeIDictionary<TKey, IEnumerable<TValue>>.Bear in mind that
ToLookupis a "do it now" operation (immediate execution) whereas aGroupByis deferred. As it happens, with the way that "pull LINQ" works, when you start pullingIGroupings from the result of aGroupBy, it has to read all the data anyway (because you can't switch group midway through) whereas in other implementations it may be able to produce a streaming result. (It does in Push LINQ; I would expect LINQ to Events to be the same.)