Group by no LINQ: entendendo o agrupamento com exemplos reais
\n\nSe você já escreveu consultas para coleções em C#, certamente já se deparou com a tarefa: "Como agrupar dados?". O tópico group by in LINQ é um dos mais populares no Stack Overflow (dezenas de milhares de visualizações). E não é por acaso: o agrupamento parece simples apenas à primeira vista. Na prática, os iniciantes tropeçam em armadilhas típicas: como selecionar o primeiro registro em um grupo, como calcular a soma ou fazer um agrupamento aninhado.
\n\nNeste artigo, vamos analisar as 5 perguntas mais frequentes sobre LINQ GroupBy e dar respostas funcionais. Vamos lá!
\n\n1. Como funciona o GroupBy no LINQ? Sintaxe básica
\n\nO método GroupBy é o análogo do operador SQL GROUP BY. Ele divide uma sequência em grupos com base em uma chave fornecida. O resultado é uma coleção de objetos IGrouping<TKey, TElement>, onde cada objeto contém a chave e todos os elementos que pertencem a esse grupo.
Aqui está um exemplo simples. Suponha que temos uma lista de alunos:
\n\npublic class Student\n{\n public string Name { get; set; }\n public string Group { get; set; }\n public int Score { get; set; }\n}\n\nvar students = new List<Student>\n{\n new Student { Name = "Anna", Group = "A", Score = 85 },\n new Student { Name = "Boris", Group = "B", Score = 90 },\n new Student { Name = "Vika", Group = "A", Score = 78 },\n new Student { Name = "Gleb", Group = "B", Score = 92 }\n};\n\nAgrupando pelo nome do grupo:
\n\nvar grouped = students.GroupBy(s => s.Group);\n\nforeach (var group in grouped)\n{\n Console.WriteLine($"Grupo: {group.Key}");\n foreach (var student in group)\n {\n Console.WriteLine($" {student.Name} — {student.Score}");\n }\n}\n\nSaída:
\nGrupo: A\n Anna — 85\n Vika — 78\nGrupo: B\n Boris — 90\n Gleb — 92\n\nTudo simples. Mas o diabo, como sempre, está nos detalhes.
\n\n2. Como selecionar o primeiro registro de cada grupo?
\n\nEsta é a pergunta mais popular sobre group by in LINQ. Suponha que precisamos obter o melhor aluno de cada grupo (com a pontuação máxima). Como fazer isso?
\n\nA solução é combinar GroupBy com First() ou OrderByDescending:
var bestStudents = students\n .GroupBy(s => s.Group)\n .Select(g => g.OrderByDescending(s => s.Score).First());\n\nforeach (var student in bestStudents)\n{\n Console.WriteLine($"{student.Name} — {student.Group} — {student.Score}");\n}\n\nResultado: Anna (85) e Gleb (92). Observe: dentro do Select, ordenamos o grupo e pegamos o primeiro elemento. Esta é a resposta para a pergunta "como fazer LINQ group by first".
Importante: First() lançará uma exceção se o grupo estiver vazio. Se isso for possível, use FirstOrDefault().
3. Como calcular a soma, a quantidade ou a média por grupo?
\n\nFrequentemente, após o agrupamento, não é apenas necessário iterar sobre os elementos, mas obter valores agregados. Por exemplo, a soma das pontuações por grupo ou a pontuação média.
\n\nUsamos Sum, Count, Average:
var groupStats = students\n .GroupBy(s => s.Group)\n .Select(g => new\n {\n Group = g.Key,\n Count = g.Count(),\n TotalScore = g.Sum(s => s.Score),\n AverageScore = g.Average(s => s.Score)\n });\n\nforeach (var stat in groupStats)\n{\n Console.WriteLine($"Grupo {stat.Group}: {stat.Count} alunos, soma {stat.TotalScore}, média {stat.AverageScore:F2}");\n}\n\nSaída:
\nGrupo A: 2 alunos, soma 163, média 81.50\nGrupo B: 2 alunos, soma 182, média 91.00\n\nTambém podemos usar Min e Max — em