1. Jeitos básicos de filtrar coleções
Filtrar é tipo uma peneira de ouro, só que ao invés de pepitas a gente pega só os elementos que precisa da coleção. Por exemplo, temos uma lista de usuários — queremos achar só os maiores de idade, ou só os estudantes, ou só quem curte café (programador, né). Selecionar os elementos certos é uma tarefa super comum, aparece em tudo: desde trabalhar com banco de dados até tratar entrada do usuário.
Bora ver jeitos diferentes de filtrar em C#. Conforme a gente for aprendendo, vamos evoluir nosso app de console de estudo, colocando filtros nele.
Filtragem manual usando loop
Vamos começar pelo jeito mais simples e clássico: filtrar usando foreach:
// Exemplo: tem uma lista de números, precisa pegar só os pares
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
List<int> evenNumbers = new List<int>();
foreach (int number in numbers)
{
if (number % 2 == 0) // se o número é par
{
evenNumbers.Add(number);
}
}
Console.WriteLine("Números pares:");
foreach (int n in evenNumbers)
{
Console.WriteLine(n);
}
Esse jeito sempre funciona, é fácil de entender, mas não é o mais compacto nem o mais "moderno".
Por que filtrar manualmente nem sempre é legal?
Quando você tem outra coleção, outro critério de filtragem, ou precisa escrever vários filtros de uma vez — acaba ficando um monte de código parecido. A gente quer algo mais compacto, legível e modular.
2. Filtrando por critérios mais complexos
Imagina que temos uma coleção de objetos. Vamos pegar o exemplo dos usuários do nosso app de estudo.
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
Declarando uma lista de usuários:
List<User> users = new List<User>
{
new User { Name = "Anya", Age = 17, IsStudent = true },
new User { Name = "Boris", Age = 21, IsStudent = false },
new User { Name = "Vika", Age = 19, IsStudent = true },
new User { Name = "Gleb", Age = 25, IsStudent = false }
};
Exemplo 1: Pegar todos os estudantes
List<User> students = new List<User>();
foreach (User user in users)
{
if (user.IsStudent)
students.Add(user);
}
Console.WriteLine("Lista de estudantes:");
foreach (User user in students)
{
Console.WriteLine($"{user.Name} ({user.Age})");
}
Exemplo 2: Pegar estudantes maiores de idade
List<User> adults = new List<User>();
foreach (User user in users)
{
if (user.Age >= 18 && user.IsStudent)
adults.Add(user);
}
Console.WriteLine("Estudantes maiores de idade:");
foreach (User user in adults)
{
Console.WriteLine($"{user.Name} ({user.Age})");
}
E se os critérios de filtragem mudam?
Dá pra colocar as condições de filtragem em métodos separados, pra chamar eles dentro do loop:
bool IsAdult(User u) { return u.Age >= 18; }
bool IsStudent(User u) { return u.IsStudent; }
List<User> filtered = new List<User>();
foreach (User user in users)
{
if (IsAdult(user) && IsStudent(user))
filtered.Add(user);
}
3. Filtrando por chave em dicionários
Listas e arrays são de boa, mas a gente também trabalha muito com dicionários.
Imagina que temos um dicionário onde a chave é o nome do usuário e o valor é a idade:
Dictionary<string, int> ageByName = new Dictionary<string, int>
{
["Anya"] = 17,
["Boris"] = 21,
["Vika"] = 19,
["Gleb"] = 25
};
Desafio: pegar só usuários 18+
Dictionary<string, int> adults = new Dictionary<string, int>();
foreach (var pair in ageByName)
{
if (pair.Value >= 18)
adults.Add(pair.Key, pair.Value);
}
Console.WriteLine("Maiores de idade:");
foreach (var pair in adults)
{
Console.WriteLine($"{pair.Key}: {pair.Value} anos");
}
Desafio: pegar usuários com nomes que começam com "V"
Dictionary<string, int> namesWithV = new Dictionary<string, int>();
foreach (var pair in ageByName)
{
if (pair.Key.StartsWith("V"))
namesWithV.Add(pair.Key, pair.Value);
}
Console.WriteLine("Nomes começando com 'V':");
foreach (var pair in namesWithV)
{
Console.WriteLine($"{pair.Key}: {pair.Value} anos");
}
4. Princípios da filtragem
Como funciona o processo de filtragem
┌──────────────────────────────────┐
│ Lista: [1, 2, 3, 4, 5, 6] │
└──────────────────────────────────┘
│
▼
[Checagem: n % 2 == 0]
│
▼
┌──────────────────────────────────┐
│ Resultado: [2, 4, 6] │
└──────────────────────────────────┘
Combinando filtros: filtrando por várias condições
Dá pra fazer filtragens em sequência, se quiser selecionar os dados por etapas:
// Primeiro pega só os estudantes
List<User> onlyStudents = new List<User>();
foreach (User user in users)
{
if (user.IsStudent)
onlyStudents.Add(user);
}
// Depois, entre eles, pega só os maiores de idade
List<User> adultStudents = new List<User>();
foreach (User user in onlyStudents)
{
if (user.Age >= 18)
adultStudents.Add(user);
}
Ou tudo de uma vez:
List<User> adultStudents = new List<User>();
foreach (User user in users)
{
if (user.IsStudent && user.Age >= 18)
adultStudents.Add(user);
}
Ordenação e outras operações
Dá pra ordenar usando o método Sort nas listas:
// Estudantes maiores de idade, ordenados por idade
adultStudents.Sort((a, b) => a.Age.CompareTo(b.Age));
Vamos falar mais sobre ordenação na próxima aula!
5. Filtragem em cenários de usuário
1. Filtrando entrada do usuário
Imagina que nosso app recebe uma lista de notas e precisa pegar só as "cinco" (nota máxima).
Console.Write("Digite as notas separadas por espaço: ");
string input = Console.ReadLine();
List<int> grades = new List<int>();
foreach (string s in input.Split(' '))
{
if (int.TryParse(s, out int grade))
grades.Add(grade);
}
List<int> fives = new List<int>();
foreach (int grade in grades)
{
if (grade == 5)
fives.Add(grade);
}
Console.WriteLine("Notas cinco:");
foreach (var grade in fives)
{
Console.WriteLine(grade);
}
2. Filtrando por várias condições
Desafio: pegar usuários estudantes com idade de 18 até 22 anos (inclusive).
List<User> filtered = new List<User>();
foreach (User user in users)
{
if (user.IsStudent && user.Age >= 18 && user.Age <= 22)
filtered.Add(user);
}
3. Filtrando pela presença de um elemento
Digamos que temos uma lista de strings e queremos pegar só as que contêm a substring ".net" (sem diferenciar maiúsculas/minúsculas).
List<string> technologies = new List<string> { "C#", ".NET", "Java", "dotnet", "JavaScript" };
List<string> netTechs = new List<string>();
foreach (var tech in technologies)
{
if (tech.ToLower().Contains(".net"))
netTechs.Add(tech);
}
foreach (var tech in netTechs)
{
Console.WriteLine(tech);
}
6. Particularidades e erros comuns na filtragem
Filtrar parece fácil, mas dá pra fazer besteira. Bora ver algumas particularidades.
Mudando a coleção original
Se você filtra a coleção original e depois muda ela depois da filtragem, — o resultado não muda, se você já criou uma nova lista. Mas se só guardou a referência pra lista antiga, mudar a coleção original pode afetar o resultado.
List<int> filtered = new List<int>();
foreach (int n in numbers)
{
if (n > 2)
filtered.Add(n);
}
// Vamos mudar a lista original
numbers.Add(10);
foreach (var n in filtered)
{
Console.WriteLine(n); // 10 não vai aparecer aqui
}
Resultado da filtragem manual
O resultado da filtragem manual normalmente é uma nova lista mutável (tipo List<T>), que você pode mexer à vontade — adicionar, remover elementos e tal.
Filtragem e performance
Os filtros nos loops são chamados pra cada elemento da coleção, então se a função dentro da condição for pesada — cuidado. Às vezes é melhor fazer um loop foreach simples, principalmente se tem lógica complicada, precisa logar ou fazer outras coisas junto.
GO TO FULL VERSION