1. コレクションの基本的なフィルタリング方法
フィルタリングって、金を探すふるいみたいなもんで、欲しい要素だけコレクションから取り出す感じ。例えばユーザーのリストがあって、成人だけ探したいとか、学生だけとか、コーヒー好き(つまりプログラマー)だけとか。こういう要素の抽出はめっちゃよくあるタスクで、DB操作からユーザー入力の処理までどこでも出てくるよ。
C#でのいろんなフィルタリング方法を見ていこう。勉強用のコンソールアプリを作りながら、フィルターを追加していくよ。
ループを使った手動フィルタリング
まずは一番シンプルでクラシックなやり方、foreachでのフィルタリングから:
// 例:数字のリストから偶数だけ選ぶ
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) // もし偶数なら
{
evenNumbers.Add(number);
}
}
Console.WriteLine("偶数:");
foreach (int n in evenNumbers)
{
Console.WriteLine(n);
}
このやり方はいつでも使えるし、分かりやすいけど、あんまりコンパクトでもないし、今風って感じでもないね。
手動フィルタリングがいつも便利とは限らない理由
別のコレクションや別のフィルター条件が出てきたり、複数のフィルターを一気に書く必要があると、似たようなコードが増えちゃう。もっとコンパクトで読みやすく、モジュール化したくなるよね。
2. 複雑な条件でのフィルタリング
例えば、オブジェクトのコレクションがあるとしよう。アプリで使うユーザーの例を使うね。
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
ユーザーのリストを宣言する:
List<User> users = new List<User>
{
new User { Name = "アーニャ", Age = 17, IsStudent = true },
new User { Name = "ボリス", Age = 21, IsStudent = false },
new User { Name = "ヴィカ", Age = 19, IsStudent = true },
new User { Name = "グレブ", Age = 25, IsStudent = false }
};
例1:全ての学生を選ぶ
List<User> students = new List<User>();
foreach (User user in users)
{
if (user.IsStudent)
students.Add(user);
}
Console.WriteLine("学生リスト:");
foreach (User user in students)
{
Console.WriteLine($"{user.Name} ({user.Age})");
}
例2:成人の学生を選ぶ
List<User> adults = new List<User>();
foreach (User user in users)
{
if (user.Age >= 18 && user.IsStudent)
adults.Add(user);
}
Console.WriteLine("成人学生:");
foreach (User user in adults)
{
Console.WriteLine($"{user.Name} ({user.Age})");
}
フィルター条件が動的な場合は?
フィルター条件を別メソッドにして、ループの中で呼び出すのもアリ:
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. 辞書でのキーによるフィルタリング
リストや配列も便利だけど、辞書を使うことも多いよね。
例えば、キーがユーザー名、値が年齢の辞書があるとする:
Dictionary<string, int> ageByName = new Dictionary<string, int>
{
["アーニャ"] = 17,
["ボリス"] = 21,
["ヴィカ"] = 19,
["グレブ"] = 25
};
課題: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("成人:");
foreach (var pair in adults)
{
Console.WriteLine($"{pair.Key}: {pair.Value}歳");
}
課題:「ヴィ」で始まる名前のユーザーを選ぶ
Dictionary<string, int> namesWithV = new Dictionary<string, int>();
foreach (var pair in ageByName)
{
if (pair.Key.StartsWith("ヴィ"))
namesWithV.Add(pair.Key, pair.Value);
}
Console.WriteLine("「ヴィ」で始まる名前:");
foreach (var pair in namesWithV)
{
Console.WriteLine($"{pair.Key}: {pair.Value}歳");
}
4. フィルタリングの原則
フィルタリングの流れ
┌──────────────────────────────────┐
│ リスト: [1, 2, 3, 4, 5, 6] │
└──────────────────────────────────┘
│
▼
[チェック: n % 2 == 0]
│
▼
┌──────────────────────────────────┐
│ 結果: [2, 4, 6] │
└──────────────────────────────────┘
フィルターの合成:いろんな条件でフィルタリング
段階的にデータを絞り込みたいときは、順番にフィルタリングしてもOK:
// まず学生だけフィルタリング
List<User> onlyStudents = new List<User>();
foreach (User user in users)
{
if (user.IsStudent)
onlyStudents.Add(user);
}
// その中から成人だけ選ぶ
List<User> adultStudents = new List<User>();
foreach (User user in onlyStudents)
{
if (user.Age >= 18)
adultStudents.Add(user);
}
一気にやるなら:
List<User> adultStudents = new List<User>();
foreach (User user in users)
{
if (user.IsStudent && user.Age >= 18)
adultStudents.Add(user);
}
ソートや他の操作
リストのソートはSortメソッドでできるよ:
// 年齢順にソートされた成人学生
adultStudents.Sort((a, b) => a.Age.CompareTo(b.Age));
ソートについては次のレクチャーで詳しくやるよ!
5. ユーザーシナリオでのフィルタリング
1. ユーザー入力のフィルタリング
例えば、アプリが点数リストを受け取って、全ての「5」を選ぶ必要があるとする。
Console.Write("スペース区切りで点数を入力して: ");
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("5点:");
foreach (var grade in fives)
{
Console.WriteLine(grade);
}
2. 複数条件でのフィルタリング
課題:18歳から22歳まで(含む)の学生ユーザーを選ぶ。
List<User> filtered = new List<User>();
foreach (User user in users)
{
if (user.IsStudent && user.Age >= 18 && user.Age <= 22)
filtered.Add(user);
}
3. 要素の有無によるフィルタリング
文字列リストから、".net"(大文字小文字問わず)を含むものだけ選びたいとき。
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. フィルタリングの特徴とよくあるミス
フィルタリングは簡単そうだけど、意外とやらかしがち。いくつかポイントを見てみよう。
元のコレクションの変更
フィルタリングした後に元のコレクションを変更しても、もう新しいリストを作ってたら結果は変わらない。でも、元のリストの参照だけ覚えてる場合は、元のコレクションの変更が結果に影響することもあるよ。
List<int> filtered = new List<int>();
foreach (int n in numbers)
{
if (n > 2)
filtered.Add(n);
}
// 元のリストに変更を加える
numbers.Add(10);
foreach (var n in filtered)
{
Console.WriteLine(n); // 10はここに入らない
}
手動フィルタリングの結果
手動フィルタリングの結果は、たいてい新しい変更可能なリスト(例えばList<T>)になるから、追加や削除とか何でもできるよ。
フィルタリングとパフォーマンス
ループ内のフィルターはコレクションの全要素に対して呼ばれるから、条件の中の関数が重い場合は注意。ロギングや追加処理が必要なときは、シンプルなforeachで書いた方が楽なこともあるよ。
GO TO FULL VERSION