CodeGym /Cursos /C# SELF /Filtrado de datos con Wher...

Filtrado de datos con Where

C# SELF
Nivel 31 , Lección 2
Disponible

1. Introducción

Filtrar es seleccionar de una colección solo aquellos elementos que cumplen una condición concreta. Imagina: en tu base de datos hay 10 000 productos, pero el manager necesita “solo” 12 que cumplen un nuevo criterio raro. Escribir cada vez un bucle a mano y un montón de comprobaciones — no es nada divertido, y el código se vuelve ilegible. LINQ se encarga de todo eso por ti.

En aplicaciones reales, el filtrado es la operación más común con los datos: mostramos al usuario solo los registros que le interesan, “limpiamos” la colección de elementos no deseados, hacemos selecciones para informes, búsquedas o envíos de e-mails. En entrevistas, las preguntas sobre filtrado con LINQ salen todo el rato. Entender este tema es clave para cualquier junior .NET developer.

2. Conociendo el método Where

Sentido y principio de funcionamiento

El método Where es un método de extensión de LINQ que recibe una función (o una lambda) que define la condición de filtrado. Devuelve una nueva secuencia (¡sin modificar la original!), que contiene solo los elementos para los que la condición devuelve true.

Firma principal:

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate)

No te asustes por los generics ni por palabras raras. Ahora lo importante es pillar la idea general: Where recibe la colección original y la condición que tú defines. En la práctica es fácil:

  • source — la colección que filtramos (por ejemplo, List<Product>)
  • predicate — la función/condición (por ejemplo, p => p.Price > 100)

Un ejercicio sencillo para empezar

Supón que tienes una lista de productos y quieres seleccionar solo los que tienen un precio mayor que 100. En los viejos tiempos escribirías un bucle foreach y añadirías a mano los elementos que tocan a una nueva lista. LINQ te deja hacerlo en una sola línea.

3. Filtrado

Nuestra clase Product y la colección inicial

En el módulo anterior escribimos una clase de productos más o menos así:


class Product
{
    public string Name { get; set; }
    public double Price { get; set; }

    // Para que el output quede bonito:
    public override string ToString()
    {
        return $"{Name} (precio: {Price})";
    }
}

Supón que tenemos una lista de productos:


var products = new List<Product>
{
    new Product { Name = "Pan", Price = 30 },
    new Product { Name = "Leche", Price = 87 },
    new Product { Name = "Queso", Price = 250 },
    new Product { Name = "Chocolate", Price = 130 }
};

Filtrar productos de más de 100 usando Where


var expensiveProducts = products.Where(p => p.Price > 100);

foreach (var product in expensiveProducts)
{
    Console.WriteLine(product);
}

¿Qué pasa por debajo?

  • Where recorre todos los elementos de la lista products,
  • Para cada uno llama a la función (en nuestro caso: p => p.Price > 100),
  • Si la función devuelve true, el producto va a la nueva colección.

Resultado en pantalla:

Queso (precio: 250)
Chocolate (precio: 130)

Fíjate: ¡la lista original products no cambia! LINQ no destroza tus datos.

4. Filtrado perezoso (Deferred Execution)

Un detalle interesante e importante: el resultado de Where solo se calcula cuando realmente accedes a los elementos. Por ejemplo, en cuanto empieza el bucle foreach, LINQ “va” por la colección original y filtra los elementos al vuelo. Si no usas el resultado, no se hace ningún trabajo. Esto se llama ejecución diferida (deferred execution).

Esto nos da un montón de ventajas:

  • Puedes montar cadenas muy largas de métodos (por ejemplo, filtrar y ordenar), y solo cuando haces la primera consulta real al resultado, se ejecuta todo.
  • Ahorro de memoria: no hay conversiones intermedias innecesarias de colecciones.
  • Si hace falta, puedes cancelar la ejecución o “parar” después de encontrar los elementos que buscas.

Esquema visual


Colección original       —►   Where (condición)    —►   Solo recorremos los elementos que tocan
    
[ x ] [ x ] [ + ] [ + ]  —►   p.Price > 100      —►   [ + ] [ + ]

(x — elemento que no cumple la condición, + — cumple)

5. Variantes de sintaxis

Sintaxis clásica de método de extensión (Method Syntax)

Este estilo ya lo estamos usando:

var result = products.Where(p => p.Price > 100);

Alternativa: Query Syntax

LINQ soporta una sintaxis parecida a las consultas SQL:


var result = from p in products
             where p.Price > 100
             select p;

¡El resultado es el mismo!
Elegir la sintaxis es cuestión de gustos, pero el method syntax (los métodos con punto) se ve más a menudo, sobre todo en proyectos reales y cuando montas cadenas largas.

6. Condiciones de filtrado complejas

Varias condiciones

Puedes usar operadores lógicos (&&, ||, !):


// Buscar todos los productos caros, menos "Chocolate"
var filtered = products.Where(
    p => p.Price > 100 && p.Name != "Chocolate"
);

Filtrar por string (subcadena, mayúsculas, Contains)


// Productos cuyo nombre contiene la letra "l"
var lProducts = products.Where(p => p.Name.Contains("l"));

Ojo: Contains distingue entre mayúsculas y minúsculas. Si quieres “sin distinguir”, puedes hacer así:


var lProducts = products.Where(p => p.Name
    .ToLower().Contains("l")); // ahora "Leche" y "Chocolate" entran en la selección

Filtrar por varias colecciones

Supón que tienes dos listas — productos y pagos. Puedes filtrar productos que aparecen en la lista de pagos — pero para eso es mejor usar los métodos Any y Join, de los que hablaremos más adelante. Solo ten en cuenta: ¡LINQ puede filtrar de formas más complejas también!

7. Detalles útiles

Filtrado anidado y cadenas de métodos

Puedes encadenar varios pasos de filtrado, llamando a Where varias veces seguidas:


var filtered = products
    .Where(p => p.Price > 100)
    .Where(p => p.Name.StartsWith("C"));

Pero es mejor juntar las condiciones en un solo Where usando operadores lógicos — así es más eficiente y el código queda más limpio.

Comparadores integrados y funciones personalizadas

A veces los operadores estándar no bastan. Por ejemplo, necesitas filtrar strings sin distinguir mayúsculas y con cultura. En ese caso, es útil usar métodos de comparación:


var rusProducts = products.Where(
    p => p.Name.StartsWith("c", StringComparison.OrdinalIgnoreCase)
);

8. Filtrado y entrada del usuario

¡Vamos a hacer un filtrado sencillo por consola! Por ejemplo, preguntamos al usuario el precio mínimo y luego mostramos todos los productos que encajan.


Console.Write("¿Precio mínimo del producto? ");
var minPriceStr = Console.ReadLine();
if (double.TryParse(minPriceStr, out double minPrice))
{
    var filtered = products.Where(p => p.Price >= minPrice);
    foreach (var product in filtered)
    {
        Console.WriteLine(product);
    }
}
else
{
    Console.WriteLine("Error: no has introducido un número.");
}

¡Ahora nuestra “mini-app” ya casi parece una tienda!

9. Errores típicos y trampas al usar Where

En programación, como en la vida, hay trampas. Aquí tienes de lo que hay que cuidarse.

Olvidar llamar a ToList() o ToArray()

El resultado de Where no es una lista ni un array. Es un objeto IEnumerable<Product>. Normalmente va perfecto en un bucle foreach, pero si necesitas una colección de verdad (por ejemplo, si quieres acceso por índice), no olvides llamar a .ToList() o .ToArray():


var filteredList = products.Where(p => p.Price > 100).ToList();

Si pasas de esto, es fácil que te salga un error tipo "Collection was modified during enumeration" — sobre todo si decides modificar la colección original mientras la recorres.

Modificar la colección original

Como el filtrado LINQ es diferido, si en medio del bucle borras elementos de la lista original, puede saltar una excepción. Esto pasa sobre todo en escenarios multihilo o cuando la colección cambia justo mientras filtras.

Valores null y predicados

Si la colección tiene null y en el predicado accedes a un campo del objeto sin comprobar, te comes un NullReferenceException. Por ejemplo:


var filtered = products.Where(p => p.Name.StartsWith("A"));

Si algún elemento de products es null, el código peta.

Recomendación: siempre añade una comprobación de null si hay riesgo de que haya elementos así:


var filtered = products.Where(p => p != null && p.Name.StartsWith("A"));
2
Tarea
C# SELF, nivel 31, lección 2
Bloqueada
Filtrado de objetos por varios criterios
Filtrado de objetos por varios criterios
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION