【C#】LINQのExceptメソッドで2つのリストの差分(差集合)を抽出する

2つのコレクションを比較し、「片方にあって、もう片方にはないデータ」を抽出したい場合、数学でいう「差集合(Relative Complement)」の考え方が必要になります。これは、リストAからリストBに含まれる要素を引き算するイメージです。

C#のLINQには、この操作を行うためのExceptメソッドが用意されています。ここでは、プロジェクト管理における「全タスク」と「完了済みタスク」のリストを比較し、「残りの未完了タスク」を洗い出すシナリオを題材に、その使い方と注意点を解説します。


目次

Exceptメソッドの概要

Exceptメソッドは、2つのシーケンス(配列やリスト)の差集合を返します。具体的には、「呼び出し元のコレクション」から「引数で指定したコレクション」に含まれる要素を除外したものを生成します。

数式で表すと $A – B$ の操作にあたります。

  • 残るもの: 第1リストに存在し、第2リストに存在しない要素。
  • 消えるもの: 第2リストに存在する要素すべて。

実践的なコード例:未完了タスクの抽出

以下のコードは、プロジェクトの全工程リストから、すでに完了した工程のリストを引き算し、これから取り組むべき作業(未完了タスク)を特定する例です。

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

namespace ProjectManagement
{
    class Program
    {
        static void Main()
        {
            // シナリオ:
            // プロジェクトの進捗確認を行う。
            // 「全工程リスト」から「完了報告があった工程」を除外し、
            // 「残りの作業(未完了タスク)」をリストアップする。

            // プロジェクトの全工程リスト
            var allTasks = new[]
            {
                "要件定義",
                "基本設計",
                "詳細設計",
                "実装",
                "単体テスト",
                "結合テスト"
            };

            // 完了した工程リスト
            var completedTasks = new[]
            {
                "要件定義",
                "基本設計",
                "詳細設計"
            };

            // 1. Exceptを使用して差集合(未完了タスク)を取得
            // allTasks (A) - completedTasks (B) = 残りのタスク
            IEnumerable<string> remainingTasks = allTasks.Except(completedTasks);

            // 結果の出力
            Console.WriteLine("--- 今後の作業予定(未完了タスク) ---");
            foreach (var task in remainingTasks)
            {
                Console.WriteLine($"- {task}");
            }
        }
    }
}

実行結果

--- 今後の作業予定(未完了タスク) ---
- 実装
- 単体テスト
- 結合テスト

技術的なポイントと注意点

1. 順序の重要性(A – B と B – A は異なる)

Exceptメソッドを使用する際、もっとも注意すべき点は「どちらからどちら引くか」です。

  • listA.Except(listB) は、「AにあってBにないもの」を返します。
  • listB.Except(listA) は、「BにあってAにないもの」を返します。

Union(和集合)やIntersect(積集合)とは異なり、順序を入れ替えると結果がまったく異なるものになるため、実装時には「引く数」と「引かれる数」を明確に意識する必要があります。

2. 重複の自動排除

Exceptメソッドも他の集合演算メソッドと同様に、結果セットを一意(ユニーク)にします。もしallTasksの中に誤って「実装」という文字が2回含まれていたとしても、Exceptを通した結果には「実装」は1つしか出力されません。

3. 文字列の比較ルール

デフォルトの比較では、文字列の大文字・小文字は厳密に区別されます。例えば、完了リストに”design”(小文字)、全リストに”Design”(大文字)があった場合、これらは別物とみなされ、除外されません。

大文字小文字を無視して除外したい場合は、第2引数にStringComparer.OrdinalIgnoreCaseを指定します。

// 大文字小文字を無視して差分を取る場合
var remaining = allTasks.Except(completedTasks, StringComparer.OrdinalIgnoreCase);

まとめ

Exceptメソッドは、特定の除外リストに基づいてデータをフィルタリングする際に非常に有用です。「全体のリスト」から「除外したいリスト」を差し引くというロジックは、タスク管理だけでなく、アクセス権限の制御や、既に処理済みのファイルのスキップなど、多くの場面で応用可能です。順序(どちらが主か)に注意して利用してください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次