1. Cách lọc collection cơ bản
Lọc giống như cái rây đãi vàng, chỉ khác là thay vì vàng thì mình chọn ra từ collection những phần tử mình cần. Ví dụ, có một danh sách user — muốn tìm chỉ những ai đủ tuổi, hoặc chỉ sinh viên, hoặc chỉ ai thích cà phê (ý là coder đó). Việc chọn phần tử phù hợp là task cực kỳ phổ biến, gặp ở khắp nơi: từ làm việc với DB đến xử lý input người dùng.
Cùng xem các cách lọc khác nhau trong C#. Khi học, mình sẽ phát triển app console học tập, thêm filter vào dần dần.
Lọc thủ công bằng vòng lặp
Bắt đầu với cách đơn giản và cổ điển nhất: lọc bằng foreach:
// Ví dụ: có danh sách số, cần chọn số chẵn
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) // nếu số là số chẵn
{
evenNumbers.Add(number);
}
}
Console.WriteLine("Số chẵn:");
foreach (int n in evenNumbers)
{
Console.WriteLine(n);
}
Cách này luôn chạy được, dễ hiểu, nhưng không gọn và không "hiện đại" lắm.
Tại sao lọc thủ công không phải lúc nào cũng tiện?
Khi bạn có collection khác, tiêu chí lọc khác, hoặc cần viết nhiều filter cùng lúc — sẽ ra rất nhiều code giống nhau. Muốn code gọn, dễ đọc và dễ tách module hơn.
2. Lọc theo tiêu chí phức tạp
Giả sử có collection object. Lấy ví dụ user trong app học tập của mình.
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
Khai báo danh sách user:
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 }
};
Ví dụ 1: Chọn tất cả sinh viên
List<User> students = new List<User>();
foreach (User user in users)
{
if (user.IsStudent)
students.Add(user);
}
Console.WriteLine("Danh sách sinh viên:");
foreach (User user in students)
{
Console.WriteLine($"{user.Name} ({user.Age})");
}
Ví dụ 2: Chọn sinh viên đủ tuổi
List<User> adults = new List<User>();
foreach (User user in users)
{
if (user.Age >= 18 && user.IsStudent)
adults.Add(user);
}
Console.WriteLine("Sinh viên đủ tuổi:");
foreach (User user in adults)
{
Console.WriteLine($"{user.Name} ({user.Age})");
}
Nếu tiêu chí lọc là động thì sao?
Có thể tách điều kiện lọc thành hàm riêng, gọi trong vòng lặp:
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. Lọc theo key trong dictionary
List và array thì tiện, nhưng nhiều khi mình làm việc với dictionary nữa.
Giả sử có dictionary, key là tên user, value là tuổi:
Dictionary<string, int> ageByName = new Dictionary<string, int>
{
["Anya"] = 17,
["Boris"] = 21,
["Vika"] = 19,
["Gleb"] = 25
};
Bài toán: chọn user 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("Người đủ tuổi:");
foreach (var pair in adults)
{
Console.WriteLine($"{pair.Key}: {pair.Value} tuổi");
}
Bài toán: chọn user có tên bắt đầu bằng "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("Tên bắt đầu bằng 'V':");
foreach (var pair in namesWithV)
{
Console.WriteLine($"{pair.Key}: {pair.Value} tuổi");
}
4. Nguyên lý lọc
Quy trình lọc hoạt động thế nào
┌──────────────────────────────────┐
│ Danh sách: [1, 2, 3, 4, 5, 6] │
└──────────────────────────────────┘
│
▼
[Kiểm tra: n % 2 == 0]
│
▼
┌──────────────────────────────────┐
│ Kết quả: [2, 4, 6] │
└──────────────────────────────────┘
Kết hợp filter: lọc theo nhiều điều kiện
Có thể lọc nhiều bước liên tiếp nếu muốn chọn dữ liệu từng giai đoạn:
// Đầu tiên lọc chỉ sinh viên
List<User> onlyStudents = new List<User>();
foreach (User user in users)
{
if (user.IsStudent)
onlyStudents.Add(user);
}
// Sau đó chọn trong số đó ai đủ tuổi
List<User> adultStudents = new List<User>();
foreach (User user in onlyStudents)
{
if (user.Age >= 18)
adultStudents.Add(user);
}
Hoặc làm luôn một lần:
List<User> adultStudents = new List<User>();
foreach (User user in users)
{
if (user.IsStudent && user.Age >= 18)
adultStudents.Add(user);
}
Sắp xếp và thao tác khác
Sắp xếp có thể dùng method Sort cho list:
// Sinh viên đủ tuổi, sắp xếp theo tuổi
adultStudents.Sort((a, b) => a.Age.CompareTo(b.Age));
Sắp tới sẽ nói kỹ hơn về sort ở bài sau nhé!
5. Lọc trong tình huống thực tế
1. Lọc input người dùng
Giả sử app nhận danh sách điểm, cần chọn tất cả điểm "5".
Console.Write("Nhập điểm cách nhau bằng dấu cách: ");
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("Điểm 5:");
foreach (var grade in fives)
{
Console.WriteLine(grade);
}
2. Lọc theo nhiều điều kiện
Bài toán: chọn user là sinh viên, tuổi từ 18 đến 22 (bao gồm).
List<User> filtered = new List<User>();
foreach (User user in users)
{
if (user.IsStudent && user.Age >= 18 && user.Age <= 22)
filtered.Add(user);
}
3. Lọc theo sự xuất hiện của phần tử
Giả sử có list string, muốn chọn những cái chứa substring ".net" (không phân biệt hoa thường).
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. Đặc điểm và lỗi phổ biến khi lọc
Lọc tưởng dễ mà dễ dính lỗi lắm. Cùng xem vài điểm cần chú ý.
Thay đổi collection gốc
Nếu bạn lọc collection gốc rồi sau đó thay đổi nó, — kết quả sẽ không đổi nếu bạn đã tạo list mới. Nhưng nếu chỉ lưu reference tới list cũ, thay đổi collection gốc sẽ ảnh hưởng tới kết quả.
List<int> filtered = new List<int>();
foreach (int n in numbers)
{
if (n > 2)
filtered.Add(n);
}
// Thay đổi list gốc
numbers.Add(10);
foreach (var n in filtered)
{
Console.WriteLine(n); // 10 sẽ không xuất hiện ở đây
}
Kết quả lọc thủ công
Kết quả lọc thủ công thường là list mới (ví dụ List<T>), bạn có thể thêm, xóa phần tử thoải mái.
Lọc và hiệu năng
Filter trong vòng lặp sẽ chạy cho từng phần tử, nên nếu function trong điều kiện nặng — cẩn thận nhé. Đôi khi dùng vòng lặp foreach đơn giản lại tốt hơn, nhất là khi logic phức tạp, cần log hoặc làm thêm thao tác khác.
GO TO FULL VERSION