C# Tips | コレクション・LINQ:和集合

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

はじめに:「和集合」は“全部まとめて、ダブりは1つにする”技

業務でデータを扱っていると、こういう場面がよく出てきます。

「A システムと B システムのユーザーを“全部”見たい」
「複数ファイルに分かれているコードを“まとめて一覧”にしたい」
「権限 A と権限 B を持つユーザーの“どちらかに含まれる人すべて”を対象にしたい」

こういう「両方の要素を全部集めるけれど、同じものは1回でいい」というのが、集合でいう「和集合」です。
C# / LINQ では Union を使うことで、和集合をシンプルに書けます。

ここから、初心者向けに

和集合のイメージ
Union の基本(値型・string)
オブジェクトでの和集合
実務での和集合の使いどころ

を、例題付きでかみ砕いて説明していきます。


和集合のイメージをつかむ

A ∪ B とは「A と B のどちらかにあるもの全部」

集合の世界で A ∪ B(A ユニオン B)は、

「A にあるものと B にあるものを全部集めて、同じものは1回にまとめたもの」

という意味です。

例えば、

A = {1, 2, 3}
B = {3, 4, 5}

なら、A ∪ B = {1, 2, 3, 4, 5} です。
3 は両方にありますが、結果では1回だけ出てきます。

LINQ の Union は、この「A ∪ B」をそのままやってくれるメソッドです。


基本:数値・文字列の和集合を Union で求める

int の和集合

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

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

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

var union = a.Union(b).ToList();

Console.WriteLine(string.Join(", ", union)); // 1, 2, 3, 4, 5
C#

ここでの重要ポイントは、「a.Union(b) は“a と b の要素を全部集めて、重複を1つにまとめる”」ということです。
Concat との違いがとても大事です。

var concat = a.Concat(b).ToList(); // 1, 2, 3, 3, 4, 5
C#

Concat は「ただくっつけるだけ」、
Union は「くっつけたあと、同じ値を1つにまとめる」という違いがあります。

string の和集合(コード一覧を統合する)

商品コードの一覧を統合するイメージで見てみます。

var codesFromSystemA = new[] { "A001", "A002", "A003" };
var codesFromSystemB = new[] { "A003", "A004" };

var allCodes = codesFromSystemA.Union(codesFromSystemB).ToList();

Console.WriteLine(string.Join(", ", allCodes)); // A001, A002, A003, A004
C#

「どちらかのシステムに存在するコードを全部知りたい」というときに、Union がぴったりです。

ここでの重要ポイントは、「和集合は“どちらかに含まれていれば対象”という考え方」だということです。
積集合(Intersect)が「両方にあるもの」、差集合(Except)が「片方にだけあるもの」、
和集合(Union)は「どちらかにあるもの全部」です。


オブジェクトの和集合:何をもって「同じ」とみなすか

そのまま Union すると「参照が同じかどうか」になる

クラスのリストに対して Union を使うときは注意が必要です。

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 ですが、別インスタンスです。
このまま Union すると、「参照が同じかどうか」で比較されるため、両方の U002 が残ってしまいます。

var union = list1.Union(list2).ToList(); // U001, U002(1), U002(2), U003 みたいな状態
C#

ここでの重要ポイントは、「クラスの Union はデフォルトでは“参照比較”」だということです。
業務的には「Id が同じなら同じユーザー」とみなしたいので、そのままでは使えません。

プロジェクションしてから Union する

初心者に一番分かりやすいのは、「比較したいキーだけに変換してから和集合を取る」方法です。

var ids1 = list1.Select(x => x.Id);
var ids2 = list2.Select(x => x.Id);

var unionIds = ids1.Union(ids2).ToList(); // U001, U002, U003
C#

これで、「どちらかのリストに存在するユーザー Id 一覧」が取れます。
必要なら、そこから元のリストに戻すこともできます。

var unionUsers = list1
    .Concat(list2)
    .GroupBy(u => u.Id)
    .Select(g => g.First())
    .ToList();
C#

ここでの重要ポイントは、「和集合を取りたいときは、“何をもって同一とみなすか”を先に決める」ことです。
Id なのか、コードなのか、複数項目の組み合わせなのか――それを Select で取り出してから Union する、という流れです。


実務でよくある和集合のパターン

「複数システムのコードを統合して一覧にする」

例えば、A システムと B システムの両方で管理している商品コードを、
「どちらかに存在するものは全部一覧にしたい」というケースです。

var codesA = new[] { "A001", "A002", "A003" };
var codesB = new[] { "A003", "A004", "A005" };

var allCodes = codesA.Union(codesB).ToList(); // A001, A002, A003, A004, A005
C#

この allCodes を使って、「どのコードがどのシステムにあるか」を後で調べることもできます。

「権限 A または権限 B を持つユーザーを対象にする」

権限の世界でも、和集合はよく出てきます。

var usersWithRoleA = new[] { "U001", "U002" };
var usersWithRoleB = new[] { "U002", "U003" };

var targetUsers = usersWithRoleA.Union(usersWithRoleB).ToList(); // U001, U002, U003
C#

「権限 A か B のどちらかを持っていれば対象」という条件を、
「権限 A ユーザー集合 ∪ 権限 B ユーザー集合」として表現しているイメージです。


和集合を使うときに意識してほしいこと

「重複を残したいのか、1つにまとめたいのか」をはっきりさせる

UnionConcat の違いは、実務でかなり重要です。

重複も含めて「全部のレコード」を見たいなら Concat
同じ値は1回でよく、「種類の一覧」が欲しいなら Union

例えば、「ログを全部時系列で見る」なら Concat でよく、
「登場したユーザー Id の一覧」が欲しいなら Union が向いています。

ここでの重要ポイントは、「和集合は“種類の一覧”を作るイメージ」だということです。
「レコードの一覧」ではなく、「値の集合」を扱っている感覚を持つと、選択を間違えにくくなります。

「何をもって同じとみなすか」を明文化する

オブジェクトの和集合では、必ずこう自分に問いかけてください。

Id が同じなら同じとみなすのか
コードが同じなら同じとみなすのか
コード+日付の組み合わせが同じなら同じとみなすのか

これを日本語で言えるようにしてから、Select や匿名型でキーを作り、Union を呼ぶ。
この順番を守ると、「業務のルールがそのままコードに落ちている」状態になります。


まとめ:「和集合ユーティリティ」は“世界をまとめて、ダブりを整理する道具”

和集合の本質は、

「複数の世界に散らばっている要素を全部集めて、
 同じものは1つにまとめた“全体像”を作る」

ことです。

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

a.Union(b) は「a と b のどちらかにあるもの全部(重複は1つ)」
Concat は「ただくっつけるだけで、重複もそのまま」
オブジェクトでは「何をもって同じとみなすか」を決めてから Select して Union
コード一覧統合、権限の「A または B」、複数システムの統合ビューなどに相性がいい

ここまで腹落ちしていれば、
「とりあえず Concat してから Distinct する」ような雑な書き方から卒業して、
“意図がそのまま読める LINQ ベースの和集合処理”を、落ち着いて書けるようになります。

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