はじめに:「重複除去」は“データをきれいに整えるフィルター”
業務システムでは、同じ ID のレコードが二重に入っていたり、同じメールアドレスが何度も出てきたりと、「重複データ」がよく紛れ込みます。
この重複をきれいに取り除くのが「重複除去(重複排除)」です。
C# では、LINQ を使うと「重複を消す」処理をかなりシンプルに書けます。
ここでは、初心者向けに
- 一番基本の
Distinct - 「特定のキーで重複除去」するパターン
- 自作クラスの重複除去
という順で、かみ砕いて説明していきます。
基本:Distinct で「まったく同じ値」を重複除去する
int や string の重複を消す
まずは一番シンプルなパターンです。Distinct() は、「同じ値が複数あるなら、1つだけ残す」というメソッドです。
using System;
using System.Collections.Generic;
using System.Linq;
var numbers = new List<int> { 1, 2, 2, 3, 3, 3, 4 };
var distinct = numbers.Distinct();
foreach (var n in distinct)
{
Console.WriteLine(n); // 1, 2, 3, 4
}
C#string でも同じです。
var names = new List<string> { "Alice", "Bob", "Alice", "Charlie" };
var distinctNames = names.Distinct();
foreach (var n in distinctNames)
{
Console.WriteLine(n); // Alice, Bob, Charlie
}
C#ここでの重要ポイントは、「Distinct() は“要素そのもの”が同じかどうかで判断する」ということです。int や string のような“値型・基本的な参照型”は、これだけで十分なことが多いです。
応用:特定のキーで重複除去したい(ID だけ見たい)
「同じユーザーIDなら1件にまとめたい」というケース
業務では、「オブジェクト全体が同じかどうか」ではなく、
「UserId が同じなら重複とみなしたい」といったケースがよくあります。
例えば、こんなクラスがあるとします。
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
}
C#データがこうだったとします。
var users = new List<User>
{
new User { Id = 1, Name = "Alice" },
new User { Id = 1, Name = "Alice (duplicate)" },
new User { Id = 2, Name = "Bob" },
};
C#このとき、「Id が 1 のユーザーは 1 件にしたい」という重複除去をしたいわけです。
GroupBy + First で「キーごとに1件だけ残す」
LINQ には「キーでグループ化する」GroupBy があるので、
これを使って「同じ Id ごとにまとめて、その中の先頭だけ取る」という書き方ができます。
var distinctUsers = users
.GroupBy(u => u.Id)
.Select(g => g.First());
C#GroupBy(u => u.Id) で「Id ごとにグループ化」し、Select(g => g.First()) で「各グループの先頭要素だけ取る」というイメージです。
ここでの重要ポイントは、「重複除去=“キーごとに1件だけ代表を残す”」と考えることです。GroupBy は「キーごとにまとめる」、First は「代表を1つ選ぶ」、という役割になります。
もう一歩:Distinct に「比較ルール」を教えてあげる
IEqualityComparer<T> を使って「何をもって同じとするか」を定義する
Distinct() は、第二引数に「比較ルール(IEqualityComparer<T>)」を渡すことができます。
これを使うと、「User は Id が同じなら同じとみなす」といったルールを Distinct に直接教えられます。
public class UserIdComparer : IEqualityComparer<User>
{
public bool Equals(User? x, User? y)
{
if (ReferenceEquals(x, y)) return true;
if (x is null || y is null) return false;
return x.Id == y.Id;
}
public int GetHashCode(User obj)
{
return obj.Id.GetHashCode();
}
}
C#この比較クラスを使って、Distinct を呼びます。
var distinctUsers = users.Distinct(new UserIdComparer());
C#これで、「Id が同じ User は重複とみなして 1 件だけ残す」という動きになります。
ここでの重要ポイントは、「Equals と GetHashCode をセットで定義する」ことです。Equals で「同じかどうか」、GetHashCode で「同じものは同じハッシュ値」を返すようにします。
この2つが一致していないと、Distinct の結果がおかしくなる可能性があります。
実務での使い分けのイメージ
単純な型(int, string)はそのまま Distinct
ID の一覧、メールアドレスの一覧など、
「値そのものが重複しているかどうか」を見たいだけなら、
素直に Distinct() で十分です。
var ids = new[] { 1, 1, 2, 3, 3 };
var distinctIds = ids.Distinct(); // 1, 2, 3
C#オブジェクトは「何をもって同じとするか」を決める
User, Order, Product などのオブジェクトでは、
「ID が同じなら同じ」「メールアドレスが同じなら同じ」など、
“同一性の基準”を自分で決める必要があります。
そのときの選択肢は、ざっくり言うとこの2つです。
GroupBy(key)→Select(g => g.First())Distinct(new CustomComparer())
どちらも「キーごとに1件だけ残す」という意味では同じですが、GroupBy は「キーとグループの両方を使いたいとき」、Distinct は「単に重複を消したいだけのとき」に向いています。
まとめ:「重複除去ユーティリティ」は“データを信頼できる形に整えるフィルター”
重複除去の本質は、
「同じ意味のデータが何度も出てこないように、1件にまとめる」ことです。
C# / LINQ では、
Distinct()で「値そのものが同じもの」を重複除去できるGroupBy(key).Select(First)で「キーごとに1件だけ代表を残す」ことができるDistinct(new Comparer)で「オブジェクトの“同一性ルール”を自分で定義して重複除去できる」
という道具が揃っています。
大事なのは、「何をもって“同じ”とみなすか」を先に言葉で決めてから、
それをコード(キーや comparer)に落とし込むことです。
そこさえブレなければ、LINQ の重複除去はとても強力な“業務ユーティリティ”になります。
