はじめに:「積集合」は“共通しているものだけを抜き出すフィルター”
業務でデータを扱っていると、こういう問いがよく出てきます。
「A システムと B システムの両方に登録されているユーザーは?」
「マスタにもファイルにも存在するコードだけを対象にしたい」
「複数条件を満たすものだけを抽出したい」
こういう「両方に共通して存在するもの」だけを取り出すのが、集合でいう「積集合」です。
C# / LINQ では Intersect を使うことで、積集合をシンプルに書けます。
ここから、初心者向けに
積集合のイメージIntersect の基本(値型・string)
オブジェクトでの積集合
実務での積集合の使いどころ
を、例題付きでかみ砕いて説明していきます。
積集合のイメージをつかむ
A ∩ B とは「A と B の両方にあるもの」
集合の世界で A ∩ B(A キャップ B)は、
「A にも B にも両方存在するもの全部」
という意味です。
例えば、
A = {1, 2, 3, 4}
B = {3, 4, 5}
なら、A ∩ B = {3, 4} です。
1 と 2 は A にしかない、5 は B にしかないので除外され、「両方に共通している 3 と 4」だけが残ります。
LINQ の Intersect は、この「A ∩ B」をそのままやってくれるメソッドです。
基本:数値・文字列の積集合を Intersect で求める
int の積集合
まずは一番シンプルな例から。
using System;
using System.Collections.Generic;
using System.Linq;
var a = new List<int> { 1, 2, 3, 4 };
var b = new List<int> { 3, 4, 5 };
var common = a.Intersect(b).ToList();
Console.WriteLine(string.Join(", ", common)); // 3, 4
C#ここでの重要ポイントは、「a.Intersect(b) は“a と b の両方にある要素”を返す」ということです。Intersect は「集合」として扱うので、重複は1つにまとめられます。
var x = new[] { 1, 1, 2, 3 };
var y = new[] { 1, 3, 3 };
var common2 = x.Intersect(y).ToList(); // 1, 3
C#1 が何回出てきても、「共通しているかどうか」だけを見ます。
string の積集合(コードの共通部分)
商品コードの共通部分を取るイメージで見てみます。
var masterCodes = new[] { "A001", "A002", "A003" };
var fileCodes = new[] { "A002", "A003", "A004" };
var commonCodes = masterCodes.Intersect(fileCodes).ToList();
Console.WriteLine(string.Join(", ", commonCodes)); // A002, A003
C#「マスタにもファイルにも存在するコード」だけを取り出しています。
業務でよくある「両方にあるものだけを対象にしたい」場面そのものです。
ここでの重要ポイントは、「Intersect は“共通部分だけを残すフィルター”」だということです。
差集合(Except)が「片方にだけあるもの」を出すのに対し、積集合(Intersect)は「両方にあるもの」だけを出します。
オブジェクトの積集合:何をもって「同じ」とみなすか
そのまま Intersect すると「参照が同じかどうか」になる
クラスのリストに対して Intersect を使うときは注意が必要です。
public class User
{
public string Id { get; set; } = "";
public string Name { get; set; } = "";
}
C#var list1 = new List<User>
{
new User { Id = "U001", Name = "Sato" },
new User { Id = "U002", Name = "Suzuki" },
};
var list2 = new List<User>
{
new User { Id = "U002", Name = "Suzuki" },
new User { Id = "U003", Name = "Tanaka" },
};
C#見た目は同じ U002 ですが、別インスタンスです。
このまま Intersect すると、「参照が同じかどうか」で比較されるため、共通要素なしと判定されてしまいます。
var common = list1.Intersect(list2).ToList(); // 実は空になる
C#ここでの重要ポイントは、「クラスの Intersect はデフォルトでは“参照比較”」だということです。
業務的には「Id が同じなら同じユーザー」とみなしたいので、そのままでは使えません。
プロジェクションしてから Intersect する
初心者に一番分かりやすいのは、「比較したいキーだけに変換してから積集合を取る」方法です。
var ids1 = list1.Select(x => x.Id);
var ids2 = list2.Select(x => x.Id);
var commonIds = ids1.Intersect(ids2).ToList(); // U002
C#これで、「両方に存在するユーザー Id 一覧」が取れます。
必要なら、そこから元のリストに戻すこともできます。
var commonUsers = list1
.Where(u => commonIds.Contains(u.Id))
.ToList();
C#ここでの重要ポイントは、「積集合を取りたいときは、“何をもって同一とみなすか”を先に決める」ことです。
Id なのか、コードなのか、複数項目の組み合わせなのか――それを Select で取り出してから Intersect する、という流れです。
複数項目の組み合わせで積集合を取りたいとき
例えば、「顧客コード+日付の組み合わせが共通しているものだけを取りたい」場合は、匿名型を使うと楽です。
var keys1 = list1.Select(x => new { x.CustomerCode, x.Date });
var keys2 = list2.Select(x => new { x.CustomerCode, x.Date });
var commonKeys = keys1.Intersect(keys2).ToList();
C#匿名型は「全プロパティが同じなら同一」とみなされるので、
複数項目の組み合わせで積集合を取りたいときにとても便利です。
実務でよくある積集合のパターン
「両方に存在するコードだけを対象にする」
マスタとファイルの両方に存在するコードだけを処理したい、というパターンです。
var masterCodes = new[] { "A001", "A002", "A003" };
var fileCodes = new[] { "A002", "A003", "A004" };
var targetCodes = masterCodes.Intersect(fileCodes).ToList(); // A002, A003
C#この targetCodes を使って、後続の処理を絞り込めます。
var targetMaster = master
.Where(m => targetCodes.Contains(m.Code))
.ToList();
C#ここでの重要ポイントは、「積集合で“対象範囲”を先に決めてから、詳細処理に進む」という設計です。
「そもそも両方に存在するものだけを扱う」と決めてしまうと、後のコードがかなりシンプルになります。
「複数条件を満たすユーザーだけを抽出する」イメージで使う
積集合は、「条件ごとの集合を作ってから、共通部分を取る」という考え方にも使えます。
例えば、
営業部に所属しているユーザー集合
権限“管理者”を持っているユーザー集合
の両方に属するユーザーだけを取りたい、という場合。
var salesUsers = users.Where(u => u.Department == "Sales").Select(u => u.Id);
var adminUsers = users.Where(u => u.Role == "Admin").Select(u => u.Id);
var targetUserIds = salesUsers.Intersect(adminUsers).ToList();
C#「営業部かつ管理者」という条件を、
「営業部ユーザー集合 ∩ 管理者ユーザー集合」として表現しているイメージです。
積集合を使うときに意識してほしいこと
「どっちにもあるものだけでいいのか?」を先に決める
積集合を使う前に、必ず自分に問いかけてください。
両方に存在するものだけを対象にしてよいのか
片方にしかないものも、何らかの形で扱う必要があるのか
両方にあるものだけでよいなら、Intersect でスッキリ書けます。
片方にしかないものも気にするなら、差集合(Except)やマージ(Join)と組み合わせて設計する必要があります。
「何をもって同じとみなすか」を明文化する
積集合で一番大事なのは、ここです。
Id が同じなら同じとみなすのか
コードが同じなら同じとみなすのか
コード+日付の組み合わせが同じなら同じとみなすのか
これを日本語で言えるようにしてから、Select や匿名型でキーを作り、Intersect を呼ぶ。
この順番を守ると、「業務のルールがそのままコードに落ちている」状態になります。
まとめ:「積集合ユーティリティ」は“共通部分だけをクリアに見せるレンズ”
積集合の本質は、
「2つの世界のうち、“両方に共通して存在するもの”だけを浮かび上がらせる」
ことです。
押さえておきたいポイントは、
a.Intersect(b) は「a と b の両方にあるもの」
重複は1つにまとめられる(集合として扱う)
オブジェクトでは「何をもって同じとみなすか」を決めてから Select して Intersect
マスタ vs ファイル、システム A vs システム B の共通部分抽出にとても相性がいい
ここまで腹落ちしていれば、
「for で2重ループを回して if で共通要素を探す」コードから卒業して、
“意図がそのまま読める LINQ ベースの積集合処理”を、落ち着いて書けるようになります。
