C# Tips | コレクション・LINQ:フィルタリング

C# C#
スポンサーリンク

はじめに:「フィルタリング」は“欲しいデータだけをすくい取る作業”

業務システムの一覧画面って、だいたいこういう条件がありますよね。

「部署=営業だけ」
「状態=未処理だけ」
「金額が 10,000 円以上だけ」

この「条件に合うものだけを取り出す」のが、フィルタリングです。
C# では LINQ の Where を使うと、これをとても素直に書けます。

ここでは、まず Where の基本から入り、
複数条件・任意条件・動的フィルタリングまで、初心者向けにかみ砕いて説明していきます。


基本:Where で「条件に合うものだけ」を残す

数値のフィルタリングで動きをつかむ

まずは一番シンプルな例から。

using System;
using System.Collections.Generic;
using System.Linq;

var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };

var even = numbers.Where(x => x % 2 == 0);

foreach (var n in even)
{
    Console.WriteLine(n); // 2, 4, 6
}
C#

Where(x => 条件) は、「条件が true になる要素だけを残す」という意味です。
ここでは「偶数だけ」を残しています。

重要なのは、「元の numbers は一切変わらない」ということです。
Where は“フィルタされた新しい列”を返すだけです。

文字列のフィルタリング

var names = new List<string> { "Alice", "Bob", "Charlie" };

var longNames = names.Where(x => x.Length >= 5);

foreach (var n in longNames)
{
    Console.WriteLine(n); // Alice, Charlie
}
C#

「長さ 5 文字以上」という条件でフィルタしています。
Where の中身は「bool を返す式」なら何でも書けます。


オブジェクトを業務条件でフィルタリングする

社員を「部署=営業」だけに絞る

業務では、クラスのリストを条件で絞るのがメインです。

public class Employee
{
    public int No { get; set; }
    public string Name { get; set; } = "";
    public string Department { get; set; } = ""; // "Sales", "Dev", ...
}
C#

データを用意します。

var employees = new List<Employee>
{
    new Employee { No = 1, Name = "Sato",   Department = "Sales" },
    new Employee { No = 2, Name = "Suzuki", Department = "Dev" },
    new Employee { No = 3, Name = "Tanaka", Department = "Sales" },
};
C#

「営業部だけ」に絞るには、こう書きます。

var sales = employees
    .Where(e => e.Department == "Sales");

foreach (var e in sales)
{
    Console.WriteLine($"{e.No}: {e.Name}");
    // 1: Sato
    // 3: Tanaka
}
C#

ここでの重要ポイントは、「Where の中で“業務の条件”をそのまま書く」感覚です。
e.Department == "Sales" は、「部署が Sales の社員だけ」という意味そのものです。

複数条件を組み合わせる(AND / OR)

「営業部かつ社員番号が 100 以上」など、複数条件を組み合わせたいときは、
&&(AND)や ||(OR)を使います。

var filtered = employees
    .Where(e => e.Department == "Sales" && e.No >= 100);
C#

「営業部または開発部」のような条件なら、こうです。

var filtered = employees
    .Where(e => e.Department == "Sales" || e.Department == "Dev");
C#

ここでの重要ポイントは、「Where の中身は“普通の if 条件”と同じ感覚で書ける」ということです。
if 文で書ける条件は、そのまま Where にも書けます。


任意条件・検索フォームとの付き合い方

「条件が指定されたときだけ絞り込む」パターン

検索画面では、こういう要件がよくあります。

「部署は任意(未指定なら全件)」
「名前の部分一致も任意」

このときにやりがちなのが、「if だらけのコード」です。

IEnumerable<Employee> query = employees;

if (!string.IsNullOrEmpty(department))
{
    query = query.Where(e => e.Department == department);
}

if (!string.IsNullOrEmpty(nameKeyword))
{
    query = query.Where(e => e.Name.Contains(nameKeyword));
}
C#

これは実は、かなり“正しい書き方”です。
条件が指定されているときだけ Where を足していく、というスタイルです。

ここでの重要ポイントは、「Where は何回重ねてもいい」ということです。
Where を重ねるたびに、条件が一つずつ追加されていきます。

条件をまとめて書くスタイル

同じことを、一つの Where にまとめて書くこともできます。

var filtered = employees.Where(e =>
    (string.IsNullOrEmpty(department)   || e.Department == department) &&
    (string.IsNullOrEmpty(nameKeyword) || e.Name.Contains(nameKeyword))
);
C#

「部署が未指定なら条件をスキップ」「指定されていれば一致したものだけ」というロジックを、
OR と AND の組み合わせで表現しています。

ただし、初心者には少し読みにくくなるので、
最初は「Where を if で足していく」スタイルのほうが分かりやすいことが多いです。


フィルタリングをユーティリティ化する

「条件オブジェクト」を受け取る拡張メソッド

業務が大きくなってくると、
「社員検索条件」「注文検索条件」のような“条件クラス”を作ることが増えます。

public sealed class EmployeeFilter
{
    public string? Department { get; init; }
    public string? NameKeyword { get; init; }
}
C#

これを受け取ってフィルタリングする拡張メソッドを作っておくと、
検索ロジックを一箇所にまとめられます。

public static class EmployeeFilteringExtensions
{
    public static IQueryable<Employee> ApplyFilter(
        this IQueryable<Employee> source,
        EmployeeFilter filter)
    {
        if (!string.IsNullOrEmpty(filter.Department))
        {
            source = source.Where(e => e.Department == filter.Department);
        }

        if (!string.IsNullOrEmpty(filter.NameKeyword))
        {
            source = source.Where(e => e.Name.Contains(filter.NameKeyword));
        }

        return source;
    }
}
C#

使い方はこうです。

var filter = new EmployeeFilter
{
    Department  = "Sales",
    NameKeyword = "田"
};

var result = employees
    .AsQueryable()
    .ApplyFilter(filter)
    .ToList();
C#

ここでの重要ポイントは、「フィルタリングのロジックを“名前のついた場所”に閉じ込める」ことです。
画面側は ApplyFilter を呼ぶだけで、条件の中身を意識せずに済みます。


null や空文字との付き合い方

null 安全にフィルタリングする

業務データでは、NameDepartment が null のこともあります。
そのまま e.Name.Contains(keyword) と書くと、null のときに例外になります。

安全に書くなら、こうです。

var filtered = employees
    .Where(e => !string.IsNullOrEmpty(e.Name) &&
                e.Name.Contains(keyword));
C#

あるいは、「null は空文字として扱う」という方針なら、こうも書けます。

var filtered = employees
    .Where(e => (e.Name ?? string.Empty).Contains(keyword));
C#

ここでの重要ポイントは、「フィルタ条件の中で null をどう扱うかを、先に決めておく」ことです。
“null はマッチしない”のか、“空文字として扱う”のか、業務ルールとして決めておきましょう。


実務で意識してほしいこと

「フィルタ条件は日本語で言えるようにしてから書く」

コードを書く前に、必ず言葉で整理してください。

部署が Sales の社員だけ
部署が Sales または Dev の社員
部署が指定されていればその部署だけ、指定されていなければ全件

これが言えれば、Where の中身はほぼ自動的に決まります。

「フィルタリング → ソート → ページング」の順番を守る

一覧画面では、だいたいこの順番になります。

条件で絞る(Where)
並び順を決める(OrderBy / ThenBy)
ページングする(Skip / Take)

順番を崩すと、「全件ソートしてから絞る」「ページごとに条件が変わる」など、
意図しない動きになりがちです。


まとめ:「フィルタリングユーティリティ」は“業務の問いをコードにする道具」

フィルタリングの本質は、
「業務の問い(どんな条件で絞りたいか)を、そのままコードに落とし込む」ことです。

押さえておきたいポイントは、

Where(条件) で「条件が true のものだけ」を残す
複数条件は &&(AND)と ||(OR)で素直に組み合わせる
任意条件は「if で Where を足していく」か、「未指定ならスキップする条件式」で書く
フィルタロジックは拡張メソッドやユーティリティにまとめておくと再利用しやすい

ここまで身につけば、「とりあえず全部取ってきてから if で捨てる」コードから卒業して、
“業務の条件をそのまま表現した LINQ フィルタリング”を書けるようになります。

タイトルとURLをコピーしました