CodeGym /Cursos /C# SELF /Funciones agregadas: Sum

Funciones agregadas: Sum, Count, Average, Max, Min

C# SELF
Nivel 32 , Lección 1
Disponible

1. Introducción

Cuando trabajamos con colecciones, muchas veces no solo queremos recorrer cada elemento y hacer algo, sino obtener un pequeño "resumen" de todo el conjunto de datos. Por ejemplo:

  • Contar cuántos estudiantes sacaron un cinco.
  • Saber cuántos alumnos hay en el cole.
  • Sumar cuántos puntos sacaron todos los estudiantes en el examen.
  • Encontrar la nota máxima y mínima.
  • Calcular la media de la clase.

Claro, todas estas tareas se pueden resolver con un bucle normal y una variable contador. Pero seamos sinceros: a nadie le apetece escribir veinte líneas de código para algo tan básico.

LINQ nos da un set de funciones agregadas — métodos ya hechos que cogen la colección, la recorren y te dan la respuesta: suma, media, máximo, mínimo, y a veces cosas más rebuscadas.

Aquí tienes una "tabla de agregados" rápida:

Método Qué hace Devuelve
Count()
Cuenta el número de elementos
int
Sum()
Suma los valores numéricos Depende del tipo de los elementos (
int
,
double
, ...)
Average()
Calcula la media aritmética
double
u otro tipo numérico
Max()
Busca el elemento máximo Elemento de la colección
Min()
Busca el elemento mínimo Elemento de la colección

Todos estos métodos son extension-methods para colecciones que implementan IEnumerable<T>. Más info — documentación oficial de métodos agregados LINQ.

2. Preparamos los datos para practicar

Vamos a seguir con una app sencilla para gestionar estudiantes. Imagina que tenemos esta clase:


// Modelo de estudiante
public class Student
{
    public string Name { get; set; }
    public int Grade { get; set; } // Nota en escala de cinco
    public string Email { get; set; }
}

Y una lista:


// Lista básica de estudiantes
List<Student> students = new List<Student>
{
    new Student { Name = "Iván", Grade = 5, Email = "ivan@example.com" },
    new Student { Name = "Olga", Grade = 4, Email = "olga@example.com" },
    new Student { Name = "Artem", Grade = 3, Email = "artem@example.com" },
    new Student { Name = "Daria", Grade = 5, Email = "darya@example.com" },
    new Student { Name = "Petr", Grade = 2, Email = "petr@example.com" }
};

3. Contar elementos: Count()

Estadística básica

Por ejemplo, quieres saber cuántos estudiantes tienes:


int totalStudents = students.Count(); // 5
Console.WriteLine($"Total de estudiantes: {totalStudents}");

Magia LINQ: ¡Así de fácil! El método Count() te devuelve el número de elementos en la colección.

Contar con condición

¿Y cuántos son sobresalientes?


int excellentStudents = students.Count(s => s.Grade == 5);
Console.WriteLine($"Sobresalientes: {excellentStudents}");

Aquí le pasamos a Count una lambda — solo cuenta los que cumplen la condición (s.Grade == 5). Por dentro, en LINQ es como Where(...).Count(), pero más corto y un pelín más eficiente.

¿Y si la colección está vacía?

Si la colección está vacía, Count devuelve 0 — no hay error ni nada raro.

4. Sumar valores: Sum()

Obteniendo la suma de todas las notas

Imagina que quieres saber la suma total de puntos que ha sacado la clase:


int sumOfGrades = students.Sum(s => s.Grade); // 5+4+3+5+2 = 19
Console.WriteLine($"Suma de todas las notas: {sumOfGrades}");

Sum recibe un selector (una lambda) que devuelve el valor de cada elemento.

Sumando en una colección de números

Si tienes una lista de números, no necesitas selector:


int[] numbers = { 1, 2, 3, 4, 5 };
int sum = numbers.Sum(); // 15

Errores comunes y matices

Si la colección está vacía, Sum() para tipos numéricos devuelve 0. Pero si la colección es de tipos nullable (int?, double?), Sum también funciona bien: ignora los valores null.

5. Media aritmética: Average()

Calculando la media de la clase

Lo típico: ¿cuánto ha sacado de media cada estudiante?


double averageGrade = students.Average(s => s.Grade); // (5+4+3+5+2)/5 = 3.8
Console.WriteLine($"Nota media: {averageGrade:F2}");

Average — tu colega para estadísticas. Ojo: el valor devuelto es siempre double, aunque los valores originales sean int. Así evitas perder la parte decimal.

Si no hay ningún elemento

Si la colección está vacía, llamar a Average() lanza una excepción InvalidOperationException. Es una trampa muy común: si no estás seguro de que hay algo en la colección, ¡compruébalo antes!


if (students.Any())
    Console.WriteLine(students.Average(s => s.Grade));
else
    Console.WriteLine("¡No hay datos para calcular la media!");

Media en un array de números


double avg = numbers.Average();

6. Máximo y mínimo: Max() y Min()

Quién es la estrella y quién está en el sótano

¿Quieres saber quién tira del carro en la clase y quién... bueno, da algún susto al profe? Fácil:


int maxGrade = students.Max(s => s.Grade); // 5
int minGrade = students.Min(s => s.Grade); // 2

Console.WriteLine($"Nota máxima: {maxGrade}");
Console.WriteLine($"Nota mínima: {minGrade}");

¿Y quién es ese crack?

A veces no solo importa el número, sino el nombre de quien lo ha conseguido. Pillamos el estudiante con la nota más alta:


// Cogemos el primer sobresaliente tras ordenar de mayor a menor
var bestStudent = students.OrderByDescending(s => s.Grade).First();
Console.WriteLine($"Mejor estudiante: {bestStudent.Name} ({bestStudent.Grade})");

En versiones más nuevas de .NET tienes MaxBy, que lo hace más bonito. Desde .NET 6 ya está, y en .NET 9 han añadido más cosas chulas (ver MaxBy en Microsoft Docs). Pero incluso sin MaxBy puedes apañarte con ordenar y .First().

Trampas y diferencias

Si la colección está vacía, llamar a Max() y Min() también lanza una InvalidOperationException. Así que prepárate para eso (sobre todo si no has comprobado la colección antes):


if (students.Any())
    Console.WriteLine($"Nota máxima: {students.Max(s => s.Grade)}");
else
    Console.WriteLine("No hay estudiantes para buscar el máximo.");

7. Ejemplos de "cadenas" de métodos agregados

Muchas veces los métodos agregados se usan junto con filtrado y proyección:


// Nota media entre los sobresalientes
double avgExcellent = students
    .Where(s => s.Grade == 5)
    .Average(s => s.Grade); // siempre 5, pero el ejemplo mola

// Suma de notas entre estudiantes con nota al menos 4
int sumGood = students
    .Where(s => s.Grade >= 4)
    .Sum(s => s.Grade);

// Número de emails únicos (por si acaso)
int uniqueEmails = students
    .Select(s => s.Email)
    .Distinct()
    .Count();

Aquí es donde LINQ empieza a "brillar": combinar operaciones te deja escribir código expresivo y legible, que hasta tu gato entendería (bueno, si tu gato es Junior C# Developer).

8. Comparación con el enfoque "manual": ¿por qué usar agregados?

Para entenderlo, comparemos LINQ con bucles normales usando el ejemplo de calcular la media:

Enfoque clásico:


int sum = 0;
int count = 0;
foreach (var s in students)
{
    sum += s.Grade;
    count++;
}
double average = (count != 0) ? (double)sum / count : 0;

LINQ:


double average = students.Average(s => s.Grade);

¡Hasta añadir el control de colección vacía es más fácil!

9. Matices útiles

Un poco sobre rendimiento y detalles de implementación

Los agregados de LINQ, a diferencia de la mayoría de operaciones LINQ, se ejecutan al instante. O sea, cuando llamas a Sum(), Count(), Average(), Max(), Min(), el recorrido de la colección ya ocurre en ese momento. Estos métodos no devuelven colecciones, sino un único resultado final.

Esto es importante: si haces algo pesado antes del agregado, como un filtrado o transformación compleja — solo se ejecuta una vez, justo cuando llamas al agregado.

¿Los métodos agregados soportan query-syntax?

Antes de usar métodos LINQ, mucha gente pregunta: "¿Se puede escribir todo esto en query-syntax?" Respuesta corta: el query-syntax no tiene palabras clave para agregados, pero puedes mezclarlos con method syntax:


var avg = (from s in students where s.Grade > 3 select s.Grade).Average();

Dentro de los paréntesis — query-syntax normal, y luego llamas al método agregado. ¡Se hace más de lo que crees!

10. Errores típicos usando LINQ

Error nº1: intentar usar Average(), Max() o Min() en una colección vacía.
Si la colección está vacía, Sum() y Count() devuelven 0 sin problema, pero Average(), Max() y Min() lanzan una excepción. Antes de llamar a estos métodos, asegúrate de que la colección tiene al menos un elemento.

Error nº2: pasar una lambda con la firma equivocada.
Por ejemplo, si pasas una string en vez de un número a una función agregada (Sum, Max, etc.), te dará error de compilación. Es fácil liarse con métodos anónimos.

Error nº3: comprobar el número de elementos de forma poco óptima.
El método Where(...).Count() primero crea una colección nueva y luego cuenta los elementos. Mejor usa Count(predicate) — cuenta directamente los que cumplen la condición y va más rápido.

Error nº4: ignorar los detalles de los tipos nullable al agregar.
Si sumas sobre int?, Sum() ignora los valores null. Es el comportamiento correcto, pero a veces puede darte un resultado inesperado si no lo tienes en cuenta.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION