Entendendo LINQ #2: Como usar o GroupBy
Hey, se você caiu aqui de paraquedas, esta é uma sequência de outro post
O método GroupBy
serve para agrupar elementos de uma coleção de acordo com um determinado valor (ou conjunto de valores) comum a eles e seu funcionamento é análogo à cláusula GROUP BY
dos bancos de dados relacionais.
O GroupBy
divide uma coleção em um grupo de coleções menores de acordo com o “critério de agrupamento” informado por parâmetro. De forma geral, os agrupamentos têm como propósito realizar uma ou mais agregações em cada grupo.
A primeira coisa que é preciso entender é como funciona a principal estrutura de retorno do método.
IEnumerable<IGrouping<TKey, TSource>>
Vendo a assinatura acima nota-se que o método irá retornar uma coleção de IGrouping's
. Mas o que é este IGrouping
?
IGrouping
é uma interface que herda de IEnumerable
, portanto ela é também um enumerável, uma coleção. Isso quer dizer que o método retorna uma coleção de coleções, ou melhor, uma coleção de grupos. A definição desta interface é a seguinte:
interface IGrouping<out TKey, out TElement>: IEnumerable<TElement>
{
TKey Key { get; }
}
Cada grupo vai conter todos os elementos que atendam ao mesmo critério de agrupamento e também uma propriedade Key
que carregará o valor (ou valores) em comum entre estes elementos.
Por exemplo: Ao agrupar uma lista de compras pelo mês da compra utilizando o GroupBy
, o retorno será uma nova coleção com todos os grupos de compras para cada mês diferente.
Para efeito de comparação, é possível pensar nesta estrutura como um dicionário onde os elementos sejam coleções (listas, arrays, entre outros).
IDictionary<TipoChave, IEnumerable<TipoElemento>>
É importante ter em mente que, diferentemente de um dicionário, os grupos não contêm uma propriedade Value.
Code time
Primeiro, vamos usar uma classe Compra
e criar um array de compras com alguns dados.
Como dito anteriormente, é possível agrupar as compras pelo mês da compra. Veja o código abaixo.
Também é possível agrupar as compras pelo cliente.
No código acima, fica claro que o retorno do GroupBy
é uma coleção (IEnumerable
) de IGrouping
. Ou seja, cada elemento desta coleção é uma outra coleção formada pelo conjunto de todos os elementos da coleção original que atendem um mesmo critério de agrupamento, a propriedade Key
é justamente o valor deste critério de agrupamento.
A tabela abaixo ilustra como ficam os dados na variável grupos
para os dois códigos.
Agregações
Como dito no início, é bem comum se agrupar dados para aplicar funções de agregação nestes grupos. Com LINQ, é muito simples fazer estas agregações.
No código abaixo, é produzida uma nova coleção contendo algumas informações através das funções de agregação.
É importante notar que gp
é também uma coleção, portanto é possível filtrá-la usando Where
, fazer projeções usando Select
, etc.
O código abaixo filtra por clientes que tenham mais de uma compra e, na projeção, a propriedade PiorCompra
representa a pior compra (a com menor valor) acima de $ 150.
Da mesma forma, também é possível aninhar agrupamentos, produzindo resultados mais complexos.
Pra finalizar, veja o código abaixo. Nele a lista de compras é agrupada pelo mês da compra e, posteriormente, nos grupos de cada mês, é feito um segundo agrupamento pelo cliente da compra. Além disso, para cada agrupamento (tanto por mês, quanto por cliente) são adicionadas informações extras como a quantidade, o total em compras e o maior valor dentre as compras.
O código dos exemplos pode ser executado e editado no Repl.it