Grouping with LINQ
20 August 2012, by Matthew Richards
When I was first starting out with LINQ, the method I had most trouble getting my head round was GroupBy. So I wanted to finish this series of posts with a quick how-to on grouping, in case others find the same problem.
In SQL, GROUP BY seems so straightforward. Somehow in LINQ it seems to contrive to cause problems though. But it’s easy when you know how.
Suppose you want to count the number of beers served by each pub. One way to do this is to start as follows:
GetBeerList().GroupBy(beer => beer.Pub);
The spirit of what you’re doing is obvious – group beer by pub. Simple.
The confusion starts when you try to do anything with the result. You can call Count() on it – that’ll give you the number of pubs. But a logical objective would be to convert this into an IDictionary<Pub, IEnumerable<Beer>> – i.e. a mapping from pubs to beers. That’s the sort of thing grouping should let you do, but I found it remarkably difficult when I first tried.
The answer is something like this:
return GetBeerList(). GroupBy(beer => beer.Pub). ToDictionary(group => group.Key, group => (IEnumerable<Beer>)group);
And if you understand how to work this out, then you have most of the techniques you’ll need to solve all your LINQ conundrums.
The starting point is to examine the return type of the GroupBy, which in this case is: IEnumerable<IGrouping<Pub, Beer>>. So it returns:
- A list of things – one per group
- A definition of each group – the IGrouping
The ToDictionary method can be used because it takes a list and converts it to a dictionary via two methods passed in – one to calculate the dictionary key from an item in the list, and one to calculate the dictionary value from an item in the list. In this case:
- The IGrouping has a Key property, which is the thing you grouped by. (A pub, in this case). So that’s how to get the keys for your dictionary.
- For the values in the dictionary, you want the actual lists of beers for each pub. As it happens, the IGrouping<> is this list – IGrouping<T> inherits from IEnumerable<T>, and it is the list of things that were grouped together by the GroupBy. So all you need to do is cast it down to an IEnumerable.
Now you know that, you can GroupBy things at will, and actually take advantage of the results. And if you can get your head round this one, the working of most of the rest of LINQ is fairly obvious just by looking at the method declarations.