C# PR

【C#】LinqのDistinctを使ってList内の重複した要素をなくす

アイキャッチ(c_sharp)
記事内に商品プロモーションを含む場合があります

プログラミング言語C#では、データコレクション内の重複した要素を簡単に取り除く手段が頻繁に求められます。
この際、LinqライブラリのDistinctメソッドが非常に役立ちます。

本記事では、Distinctメソッドの基本的な使い方から、参照型データの重複削除方法まで、わかりやすく解説していきます。

この記事でわかること
  • LinqのDistinctメソッドの使い方
  • 参照型のListで重複削除する方法

LinqのDistinctを使うとListや配列内のデータを一意にできる

C#のプログラミングで重複する要素を持つListや配列を扱う際、LinqのDistinctメソッドは非常に有効です。
このメソッドを利用することで、重複を効率的に排除し、データの整理がよりスムーズに行えます。
ここでは、Distinctメソッドの使い方と、それがデータをどのように一意化するのかをサンプルコードと共に解説します。


// 重複を含む整数のListを定義
var numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };

// Distinctメソッドで重複を排除し、一意の要素だけのListを作成
var distinctNumbers = numbers.Distinct().ToList();

// 一意の要素をコンソールに出力
Console.WriteLine("Unique numbers:");
foreach (var number in distinctNumbers)
{
	Console.WriteLine(number);
}

このサンプルコードでは、初めに重複を含む整数のListを作成します。
次にDistinctメソッドを使用し、重複する要素を取り除きながら一意の要素のみを含む新しいListを形成。
最後に、一意の数字をコンソールに表示することで、Distinctメソッドの効果を確認できます。

Distinctメソッドの動作原理

C#のLinqライブラリにおけるDistinctメソッドの動作を理解するには、まずEnumerableクラスのソースコードを見てみましょう。

Enumerableクラス

public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source) {
	if (source == null) throw Error.ArgumentNull("source");
	return DistinctIterator<TSource>(source, null);
}

この部分で、Distinctメソッドは与えられたソースコレクションに対して動作し、nullチェックの後、DistinctIteratorメソッドに処理を委ねています。

Enumerableクラス

static IEnumerable<TSource> DistinctIterator<TSource>(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer) {
	Set<TSource> set = new Set<TSource>(comparer);
	foreach (TSource element in source)
		if (set.Add(element)) yield return element;
}

ここでは、新しいSetオブジェクトを作成し、その中にソースからの各要素を追加しています。
セット内で重複する要素は追加されません。
つまり、既にセットに存在する要素は無視され、新しい要素のみが加えられるのです。

Setクラス

public bool Add(TElement value) {
	return !Find(value, true);
}

SetクラスのAddメソッドは、Findメソッドを使って与えられた値がセット内にすでに存在するかをチェックします。
この方法により、Distinctメソッドは各要素が一意かどうかを高速に判断できるのです。

Setクラス

bool Find(TElement value, bool add) {
	int hashCode = InternalGetHashCode(value);
	for (int i = buckets[hashCode % buckets.Length] - 1; i >= 0; i = slots[i].next) {
		if (slots[i].hashCode == hashCode && comparer.Equals(slots[i].value, value)) return true;
	}
	if (add) {
		int index;
		if (freeList >= 0) {
			index = freeList;
			freeList = slots[index].next;
		}
		else {
			if (count == slots.Length) Resize();
			index = count;
			count++;
		}
		int bucket = hashCode % buckets.Length;
		slots[index].hashCode = hashCode;
		slots[index].value = value;
		slots[index].next = buckets[bucket] - 1;
		buckets[bucket] = index + 1;
	}
	return false;
}

Findメソッドでは、ハッシュコードを利用して要素を効率的に検索し、既にセットに存在するかを判断します。
このプロセスにより、Distinctメソッドは重複を迅速に検出し、除去することが可能になります。

参照型のListで重複削除する方法

C#で参照型オブジェクトを含むListにDistinctメソッドを適用する場合、カスタムオブジェクトの比較方法とIEqualityComparer<T>インターフェースのカスタム実装に特別な注意が必要です。
ここでは、そのようなシナリオにおけるDistinctの使用方法をサンプルコードを交えて解説します。


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

class Person
{
	public string Name { get; set; }
	public int Age { get; set; }

	// 他のメンバー
}

class PersonComparer : IEqualityComparer<Person>
{
	public bool Equals(Person x, Person y)
	{
		if (Object.ReferenceEquals(x, y)) return true;
		if (x == null || y == null) return false;
		return x.Name == y.Name && x.Age == y.Age;
	}

	public int GetHashCode(Person obj)
	{
		return (obj.Name == null ? 0 : obj.Name.GetHashCode()) ^ obj.Age.GetHashCode();
	}
}

class Program
{
	static void Main()
	{
		var people = new List<Person>
		{
			new Person { Name = "Alice", Age = 30 },
			new Person { Name = "Bob", Age = 25 },
			new Person { Name = "Alice", Age = 30 }  // 重複するPersonオブジェクト
		};

		var distinctPeople = people.Distinct(new PersonComparer()).ToList();

		foreach (var person in distinctPeople)
		{
			Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
		}
	}
}

このサンプルでは、まずPersonクラスのインスタンスを含むListを作成します。
次に、カスタムIEqualityComparer<Person>実装を使って、Distinctメソッドで重複する要素を除去します。
ここでのポイントは、Personオブジェクトが重複しているかの判断をカスタムの比較ロジックで行うことです。

参照型の場合、デフォルトではオブジェクトの参照自体が比較されるため、オブジェクトの内容が同じでも異なると見なされることがあります。
このため、オブジェクトの内容に基づいて等価性を判断するカスタムの比較ロジックを定義することが重要です。
上記のPersonComparerクラスは、PersonオブジェクトのNameAgeプロパティを基に等価性を判断しています。

まとめ

この記事では、C#におけるLinqライブラリのDistinctメソッドの使い方とその効果について詳しく見てきました。
主なポイントは以下の通りです:

  • DistinctメソッドはListや配列内の重複する要素を効率的に一意のものに変換し、データ処理を容易にします。
  • 参照型オブジェクトを含むListでは、カスタムの等価性比較ロジックやIEqualityComparer<T>インターフェースの実装を用いることで、Distinctメソッドを適切に使用できます。

最後に、Distinctメソッドは、データの整理や分析を行う際に重要なツールとなります。
ただし、その使用方法はコレクションの種類やデータ型によって異なるため、各シナリオに応じて適切なアプローチを選択することが重要です。
これにより、C#プログラミングにおけるデータ処理の効率と正確性が大きく向上します。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です