Group by en LINQ: entendiendo la agrupación con ejemplos reales
\n\nSi alguna vez has escrito consultas a colecciones en C#, seguramente te has enfrentado a la tarea: «¿Cómo puedo agrupar datos?». El tema group by in LINQ es uno de los más populares en Stack Overflow (decenas de miles de visitas). Y no es casualidad: la agrupación parece sencilla solo a primera vista. En realidad, los principiantes tropiezan con obstáculos típicos: cómo seleccionar el primer registro en un grupo, cómo calcular la suma o hacer una agrupación anidada.
\n\nEn este artículo, analizaremos las 5 preguntas más frecuentes sobre LINQ GroupBy y daremos respuestas funcionales. ¡Allá vamos!
\n\n1. ¿Cómo funciona GroupBy en LINQ? Sintaxis básica
\n\nEl método GroupBy es el análogo del operador SQL GROUP BY. Divide una secuencia en grupos según una clave determinada. El resultado es una colección de objetos IGrouping<TKey, TElement>, donde cada objeto contiene la clave y todos los elementos que pertenecen a ese grupo.
Aquí tienes un ejemplo sencillo. Supongamos que tenemos una lista de estudiantes:
\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 = "Ana", 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\nAgrupamos por nombre del 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\nSalida:
\nGrupo: A\n Ana — 85\n Vika — 78\nGrupo: B\n Boris — 90\n Gleb — 92\n\nTodo sencillo. Pero el diablo, como siempre, está en los detalles.
\n\n2. ¿Cómo seleccionar el primer registro de cada grupo?
\n\nEsta es la pregunta más popular sobre group by in LINQ. Supongamos que necesitamos obtener al mejor estudiante de cada grupo (con la puntuación máxima). ¿Cómo hacerlo?
\n\nLa solución es combinar GroupBy con First() o 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: Ana (85) y Gleb (92). Observa: dentro de Select ordenamos el grupo y tomamos el primer elemento. Esta es la respuesta a la pregunta «cómo hacer LINQ group by first».
Importante: First() lanzará una excepción si el grupo está vacío. Si es posible, usa FirstOrDefault().
3. ¿Cómo calcular la suma, el conteo o el promedio por grupo?
\n\nA menudo, después de agrupar, no solo necesitas recorrer los elementos, sino obtener valores agregados. Por ejemplo, la suma de puntuaciones por cada grupo o el promedio.
\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} estudiantes, suma {stat.TotalScore}, promedio {stat.AverageScore:F2}");\n}\n\nSalida:
\nGrupo A: 2 estudiantes, suma 163, promedio 81.50\nGrupo B: 2 estudiantes, suma 182, promedio 91.00\n\nTambién puedes usar Min y Max — en