CodeGym /Cursos /C# SELF /Filtrando dados com Where<...

Filtrando dados com Where

C# SELF
Nível 31 , Lição 2
Disponível

1. Introdução

Filtrar é selecionar da coleção só aqueles elementos que passam por uma condição. Imagina: na sua base tem 10 000 produtos, mas o gerente quer “só” 12 que batem com um critério novo e maluco. Escrever um loop foreach toda vez e um monte de if — ninguém merece, e o código fica horrível. LINQ resolve esse perrengue pra você.

Na vida real, filtrar é a operação mais comum com dados: mostramos pro usuário só os registros que ele quer, “limpamos” a coleção de itens indesejados, fazemos seleções pra relatórios, buscas ou envio de e-mail. Em entrevistas, perguntas sobre filtragem com LINQ aparecem direto. Entender esse assunto é essencial pra quem tá começando com .NET.

2. Conhecendo o método Where

O que é e como funciona

O método Where é um método de extensão do LINQ que recebe uma função (ou expressão lambda) que define a condição de filtragem. Ele retorna uma nova sequência (sem mudar a original!), só com os elementos pra quem a condição retorna true.

Assinatura principal:

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

Não se assusta com generics e nomes estranhos. O importante agora é sacar a ideia: Where recebe a coleção original e a condição que você manda. Na prática é assim:

  • source — a coleção que vamos filtrar (tipo List<Product>)
  • predicate — a função/condição (tipo p => p.Price > 100)

Um exercício simples pra começar

Digamos que temos uma lista de produtos e queremos só os que custam mais de 100. Antigamente, a gente fazia um loop foreach e adicionava manualmente os itens certos numa nova lista. Com LINQ, dá pra fazer isso em uma linha.

3. Filtragem

Nosso class Product e a coleção inicial

No módulo passado, a gente fez uma classe de produtos mais ou menos assim:


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

    // Pra deixar a saída bonitinha:
    public override string ToString()
    {
        return $"{Name} (preço: {Price})";
    }
}

Digamos que temos uma lista de produtos:


var products = new List<Product>
{
    new Product { Name = "Pão", Price = 30 },
    new Product { Name = "Leite", Price = 87 },
    new Product { Name = "Queijo", Price = 250 },
    new Product { Name = "Chocolate", Price = 130 }
};

Filtrando produtos acima de 100 com Where


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

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

O que rola por trás dos panos:

  • Where passa por todos os itens da lista products,
  • Pra cada um, chama a função (no nosso caso: p => p.Price > 100),
  • Se a função retorna true, o produto entra na nova coleção.

O que aparece na tela:

Queijo (preço: 250)
Chocolate (preço: 130)

Repara: a lista original products não mudou! LINQ não destrói seus dados.

4. Filtragem preguiçosa (Deferred Execution)

Um detalhe importante: o resultado do Where só é calculado quando você realmente acessa os itens. Por exemplo, quando começa o loop foreach, o LINQ “anda” pela coleção original e filtra os itens na hora. Se você não usar o resultado, nada acontece. Isso se chama execução preguiçosa (deferred execution).

Isso traz várias vantagens:

  • Dá pra montar cadeias enormes de métodos (tipo filtrar e ordenar), e só quando você realmente pede o resultado é que tudo roda.
  • Economiza memória: não cria coleções intermediárias à toa.
  • Se precisar, dá pra cancelar a execução ou “parar” depois de achar o tanto de itens que você queria.

Esquema visual


Coleção original         —►   Where (condição)    —►   Só passamos pelos itens certos
    
[ x ] [ x ] [ + ] [ + ]  —►   p.Price > 100      —►   [ + ] [ + ]

(x — item não passa na condição, + — passa)

5. Variações de sintaxe

Sintaxe clássica de método de extensão (Method Syntax)

Esse é o estilo que já estamos usando:

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

Alternativa: Query Syntax

O LINQ aceita uma sintaxe parecida com SQL:


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

O resultado é o mesmo!
Escolher a sintaxe é questão de gosto, mas o method syntax (com ponto) aparece mais, principalmente em projetos reais e quando você faz cadeias longas de métodos.

6. Condições de filtragem mais complexas

Várias condições

Você pode usar operadores lógicos (&&, ||, !):


// Achar todos os produtos caros, menos "Chocolate"
var filtered = products.Where(
    p => p.Price > 100 && p.Name != "Chocolate"
);

Filtrando por string (substring, case, Contains)


// Produtos cujo nome tem a letra "l"
var lProducts = products.Where(p => p.Name.Contains("l"));

Atenção: Contains diferencia maiúsculas de minúsculas! Se quiser ignorar o case, faz assim:


var lProducts = products.Where(p => p.Name
    .ToLower().Contains("l")); // agora "Leite" e "Chocolate" entram na seleção

Filtrando com várias coleções

Digamos que você tem duas listas — produtos e pagamentos. Dá pra filtrar produtos que aparecem na lista de pagamentos — mas aí é melhor usar os métodos Any e Join, que a gente vai ver depois. Só pra saber: o LINQ filtra de jeitos bem mais avançados também!

7. Dicas úteis

Filtragem aninhada e cadeias de métodos

Você pode juntar várias etapas de filtragem, chamando Where várias vezes:


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

Mas é melhor juntar as condições num só Where usando operadores lógicos — fica mais eficiente e o código mais limpo.

Comparadores prontos e funções customizadas

Às vezes os operadores padrão não bastam. Por exemplo, pra filtrar strings ignorando case e cultura. Aí é bom usar métodos de comparação:


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

8. Filtragem e entrada do usuário

Bora fazer uma filtragem simples no console! Por exemplo, perguntar pro usuário o preço mínimo e mostrar os produtos que batem.


Console.Write("Preço mínimo do produto? ");
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("Erro: não foi digitado um número.");
}

Agora nosso "mini-app" já tá quase parecendo uma lojinha!

9. Erros comuns e armadilhas ao usar Where

Na programação, como na vida, tem umas pegadinhas. Olha só o que evitar.

Esqueceu de chamar ToList() ou ToArray()

O resultado do Where não é uma lista nem um array! É um objeto IEnumerable<Product>. Na maioria das vezes, funciona de boa no foreach, mas se você precisa de uma coleção de verdade (tipo pra acessar por índice), não esquece de chamar .ToList() ou .ToArray():


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

Se esquecer disso, pode dar erro tipo "Collection was modified during enumeration" — principalmente se você tentar mudar a coleção original durante o loop.

Mudando a coleção original

Como a filtragem do LINQ é preguiçosa, se você apagar itens da lista original no meio do loop, pode rolar exceção. Isso é ainda mais perigoso em cenários com várias threads ou quando a coleção muda durante a filtragem.

Valores null e predicados

Se a coleção tem null e você tenta acessar um campo do objeto sem checar, vai tomar um NullReferenceException. Tipo:


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

Se algum item em products for null, o código quebra.

Dica: sempre coloca uma checagem de null se tiver chance de aparecer:


var filtered = products.Where(p => p != null && p.Name.StartsWith("A"));
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION