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プロパティを基に等価性を判断しています。

キャリア形成/給与還元
ひとつひとつ真摯に向き合う企業
ONE_WEDGE社員募集

株式会社 ONE WEDGEでは、新たな仲間を募集しています!

私たちと一緒に、革新的で充実したキャリアを築きませんか?
当社は、従業員が仕事と私生活のバランスを大切にできるよう、充実した福利厚生を整えています。

  • 完全週休2日制(土日休み)で、祝日や夏季休暇、年末年始休暇もしっかり保証!
  • 様々な休暇制度(有給、慶弔、産前・産後、育児、バースデー休暇)を完備!
  • 従業員の成長と健康を支援するための表彰制度、資格取得支援、健康促進手当など!
  • 生活を支えるテレワーク手当、記事寄稿手当、結婚祝金・出産祝金など、様々な手当を提供!
  • 自己啓発としての書籍購入制度や、メンバー間のコミュニケーションを深める交流費補助!
  • 成果に応じた決算賞与や、リファラル採用手当、AI手当など、頑張りをしっかり評価!
  • ワークライフバランスを重視し、副業もOK!

株式会社 ONE WEDGEでは、一人ひとりの従業員が自己実現できる環境を大切にしています。
共に成長し、刺激を与え合える仲間をお待ちしております。
あなたの能力と熱意を、ぜひ当社で発揮してください。
ご応募お待ちしております!

ホームページ、採用情報は下記ボタンからご確認ください!

応募、ご質問など、LINEでお気軽にご相談ください♪

まとめ

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

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

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

COMMENT

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