重複データの排除
データベースから取得したデータや、ユーザー入力値をリスト化する際、同じ値が複数回含まれる「重複」が発生することがあります。集計処理や一覧表示を行う前には、これらを一意(ユニーク)な状態に整理する必要があります。
C#のLINQには、この重複除去を行うためのDistinctメソッドが用意されています。また、.NET 6以降では、オブジェクトの特定のプロパティに基づいて重複を判定するDistinctByメソッドも追加され、より簡潔な記述が可能になりました。
この記事では、基本的な値型の重複除去から、オブジェクトや大文字・小文字を区別しない重複除去の方法までを解説します。
Distinct メソッドの基本(値型の重複除去)
Distinctメソッドは、シーケンス内の要素を比較し、重複している要素を取り除いた新しいシーケンスを返します。intやstringなどの基本的な型であれば、引数なしで呼び出すだけで機能します。
結果として返される要素の順序は、通常「最初に出現した順」が維持されます。
コード例:数値配列の重複除去
整数の配列から重複を取り除き、さらに昇順に並べ替える例です。
using System;
using System.Collections.Generic;
using System.Linq; // LINQを使用するために必須
public class DistinctBasicExample
{
public static void Main()
{
// 重複を含む数値配列
int[] rawData = { 10, 50, 30, 10, 20, 50, 40 };
// 1. Distinct で重複を除去
// 2. OrderBy で昇順にソート
var uniqueData = rawData.Distinct().OrderBy(x => x);
Console.WriteLine("--- 重複除去後のデータ ---");
Console.WriteLine(string.Join(", ", uniqueData));
}
}
出力結果:
--- 重複除去後のデータ ---
10, 20, 30, 40, 50
DistinctBy メソッド(オブジェクトの特定のプロパティで判定)
クラス(オブジェクト)のリストに対してDistinct()を使用する場合、デフォルトでは「参照(メモリ上の位置)」が比較されるため、プロパティの値が同じでも「別のインスタンス」であれば重複とはみなされません。
特定のプロパティ(例:IDやカテゴリ名)を基準に重複を除去したい場合、.NET 6で導入された**DistinctBy**メソッドを使用するのが最も効率的です。
コード例:カテゴリによる重複除去
商品リストから、「カテゴリ」が重複しないように代表的な商品を1つずつ抽出する例です。
using System;
using System.Collections.Generic;
using System.Linq;
// 商品クラス
public class ProductItem
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
}
public class DistinctByExample
{
public static void Main()
{
var products = new List<ProductItem>
{
new ProductItem { Id = 1, Name = "Laptop", Category = "Electronics" },
new ProductItem { Id = 2, Name = "T-Shirt", Category = "Clothing" },
new ProductItem { Id = 3, Name = "Smartphone", Category = "Electronics" }, // カテゴリ重複
new ProductItem { Id = 4, Name = "Jeans", Category = "Clothing" }, // カテゴリ重複
new ProductItem { Id = 5, Name = "Desk", Category = "Furniture" }
};
// Category プロパティをキーとして重複を除去
// 各カテゴリから最初に見つかった要素が残る
var uniqueCategories = products.DistinctBy(p => p.Category);
Console.WriteLine("--- カテゴリごとの代表商品 ---");
foreach (var item in uniqueCategories)
{
Console.WriteLine($"カテゴリ: {item.Category}, 商品: {item.Name}");
}
}
}
出力結果:
--- カテゴリごとの代表商品 ---
カテゴリ: Electronics, 商品: Laptop
カテゴリ: Clothing, 商品: T-Shirt
カテゴリ: Furniture, 商品: Desk
(注: .NET Frameworkなどの古い環境ではDistinctByが使用できないため、GroupByを使用するか、IEqualityComparerを実装したクラスをDistinctに渡す必要があります。)
応用:大文字・小文字を区別しない重複除去
stringの配列において、"admin"と"ADMIN"を「同じ」とみなして重複を除去したい場合があります。
DistinctメソッドのオーバーロードにStringComparerを渡すことで、比較ルールを変更できます。
コード例:大文字・小文字を無視
using System;
using System.Collections.Generic;
using System.Linq;
public class DistinctIgnoreCaseExample
{
public static void Main()
{
var emails = new[] { "user@example.com", "USER@EXAMPLE.COM", "admin@example.com" };
// StringComparer.OrdinalIgnoreCase を指定して比較
var uniqueEmails = emails.Distinct(StringComparer.OrdinalIgnoreCase);
Console.WriteLine("--- ユニークなメールアドレス ---");
foreach (var email in uniqueEmails)
{
Console.WriteLine(email);
}
}
}
出力結果:
--- ユニークなメールアドレス ---
user@example.com
admin@example.com
大文字の"USER@EXAMPLE.COM"は重複とみなされ、最初に出現した小文字の方が採用されています。
まとめ
LINQを使用することで、コレクションの重複除去を簡潔に記述できます。
Distinct(): 単純な値型(int,stringなど)の重複除去に使用します。DistinctBy(x => x.Prop): オブジェクトの特定のプロパティを基準に重複を除去する場合に使用します。(.NET 6以降)Distinct(StringComparer...): 文字列の大文字・小文字を無視したい場合に使用します。
データのクレンジングや、プルダウンメニューの選択肢生成など、重複のないリストが必要な場面で非常に有用な機能です。
