CodeGym /コース /C# SELF /要素のフィルタリング

要素のフィルタリング

C# SELF
レベル 29, レッスン 0
使用可能

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で書いた方が楽なこともあるよ。

2
タスク
C# SELF, レベル 29, レッスン 0
ロック未解除
辞書の値によるフィルタリング
辞書の値によるフィルタリング
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION