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