[C#/LINQ] Distinctメソッドの使い方 – 重複要素の削除

C#のLINQにおけるDistinctメソッドは、コレクション内の重複要素を削除し、一意の要素のみを返すために使用されます。

DistinctはデフォルトでオブジェクトのEqualsメソッドGetHashCodeメソッドを使用して重複を判断します。

例えば、整数のリストList<int> numbers = new List<int> { 1, 2, 2, 3 };に対してnumbers.Distinct()を適用すると、{ 1, 2, 3 }が返されます。

カスタムオブジェクトの場合は、IEqualityComparer<T>を実装して独自の比較ロジックを定義できます。

この記事でわかること
  • Distinctメソッドの基本的な使い方
  • 重複削除のカスタマイズ方法
  • LINQメソッドとの組み合わせ
  • Distinctメソッドの制約と注意点
  • 代替手段としてのHashSetやGroupBy

目次から探す

Distinctメソッドとは

C#のLINQ(Language Integrated Query)におけるDistinctメソッドは、コレクション内の重複要素を削除し、ユニークな要素のみを取得するための便利なメソッドです。

このメソッドは、IEnumerable<T>インターフェースを実装したコレクションに対して使用でき、特に配列やリストなどのデータ構造でよく利用されます。

Distinctメソッドは、元のコレクションの順序を保持しつつ、重複を排除した新しいコレクションを返します。

これにより、データの集計やフィルタリングを行う際に非常に役立ちます。

例えば、ユーザーのリストから重複したユーザーを削除したり、特定の条件に基づいてユニークな値を抽出したりすることが可能です。

このメソッドは、デフォルトではオブジェクトの参照を基に重複を判断しますが、カスタム比較を行うためにIEqualityComparerを使用することもできます。

これにより、特定のプロパティに基づいて重複を削除することが可能になります。

Distinctメソッドの基本的な使い方

数値型コレクションでの使用例

数値型のコレクションに対してDistinctメソッドを使用することで、重複した数値を削除し、ユニークな数値のリストを取得できます。

以下は、整数のリストから重複を削除する例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
        
        // Distinctメソッドを使用して重複を削除
        var distinctNumbers = numbers.Distinct();
        
        foreach (var number in distinctNumbers)
        {
            Console.WriteLine(number);
        }
    }
}
1
2
3
4
5

文字列型コレクションでの使用例

文字列型のコレクションでも、Distinctメソッドを使用して重複した文字列を削除できます。

以下は、文字列のリストから重複を削除する例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<string> fruits = new List<string> { "apple", "banana", "apple", "orange", "banana" };
        
        // Distinctメソッドを使用して重複を削除
        var distinctFruits = fruits.Distinct();
        
        foreach (var fruit in distinctFruits)
        {
            Console.WriteLine(fruit);
        }
    }
}
apple
banana
orange

オブジェクト型コレクションでの使用例

オブジェクト型のコレクションに対してDistinctメソッドを使用する場合、デフォルトではオブジェクトの参照が比較されます。

カスタムクラスを使用する場合は、IEqualityComparerを実装する必要があります。

以下は、カスタムクラスの例です。

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

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

// IEqualityComparer<Person>を実装して、Nameプロパティで比較する
class PersonComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        if (x == null || y == null)
            return false;
        return x.Name == y.Name;
    }

    public int GetHashCode(Person obj)
    {
        if (obj == null)
            return 0;
        return obj.Name.GetHashCode();
    }
}

class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice" },
            new Person { Name = "Bob" },
            new Person { Name = "Alice" }
        };

        // PersonComparerを使用してDistinctを実行
        var distinctPeople = people.Distinct(new PersonComparer()).ToList();

        foreach (var person in distinctPeople)
        {
            Console.WriteLine(person.Name);
        }
    }
}
Alice
Bob

実装しなかった場合、Distinctメソッドはデフォルトではオブジェクトの値ではなく参照を比較するため、期待通りに動作しません。

Distinctメソッドの戻り値の型

Distinctメソッドは、元のコレクションと同じ型のIEnumerable<T>を返します。

これにより、LINQメソッドチェーンを使用してさらに操作を続けることができます。

例えば、Distinctメソッドの後にWhereメソッドを使用して条件を追加することが可能です。

DistinctメソッドとIEnumerableの関係

DistinctメソッドはIEnumerable<T>インターフェースを実装しているため、LINQを使用しているコレクションに対して簡単に適用できます。

IEnumerable<T>は、コレクションの要素を列挙するための基本的なインターフェースであり、LINQメソッドを使用することで、データの操作や変換が容易になります。

Distinctメソッドを使用することで、コレクションの重複を簡単に管理できるため、データ処理の効率が向上します。

Distinctメソッドのカスタマイズ

IEqualityComparerを使ったカスタム比較

Distinctメソッドは、デフォルトではオブジェクトの参照を基に重複を判断しますが、IEqualityComparerインターフェースを実装することで、カスタム比較を行うことができます。

これにより、特定のプロパティに基づいて重複を削除することが可能です。

以下は、名前を基準に重複を削除する例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
}
class PersonComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name == y.Name; // 名前が同じなら等しいと判断
    }
    public int GetHashCode(Person obj)
    {
        return obj.Name.GetHashCode(); // 名前のハッシュコードを返す
    }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice" },
            new Person { Name = "Bob" },
            new Person { Name = "Alice" }
        };
        
        // IEqualityComparerを使用して重複を削除
        var distinctPeople = people.Distinct(new PersonComparer());
        
        foreach (var person in distinctPeople)
        {
            Console.WriteLine(person.Name);
        }
    }
}
Alice
Bob

カスタムクラスでDistinctを使用する方法

カスタムクラスに対してDistinctメソッドを使用する場合、IEqualityComparerを実装することが一般的ですが、クラス内でEqualsメソッドGetHashCodeメソッドをオーバーライドすることでも重複を判断できます。

以下は、カスタムクラス内での実装例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
    public override bool Equals(object obj)
    {
        if (obj is Person other)
        {
            return this.Name == other.Name; // 名前が同じなら等しいと判断
        }
        return false;
    }
    public override int GetHashCode()
    {
        return Name.GetHashCode(); // 名前のハッシュコードを返す
    }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice" },
            new Person { Name = "Bob" },
            new Person { Name = "Alice" }
        };
        
        // Distinctメソッドを使用して重複を削除
        var distinctPeople = people.Distinct();
        
        foreach (var person in distinctPeople)
        {
            Console.WriteLine(person.Name);
        }
    }
}
Alice
Bob

匿名型でのDistinctの使用

LINQでは、匿名型を使用してDistinctメソッドを適用することも可能です。

匿名型は、特定のプロパティを持つオブジェクトを簡単に作成できるため、特定の条件に基づいて重複を削除するのに便利です。

以下は、匿名型を使用した例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        var items = new List<object>
        {
            new { Name = "Alice", Age = 30 },
            new { Name = "Bob", Age = 25 },
            new { Name = "Alice", Age = 30 }
        };
        
        // 匿名型でDistinctメソッドを使用して重複を削除
        var distinctItems = items.Distinct();
        
        foreach (var item in distinctItems)
        {
            Console.WriteLine($"{item.GetType().GetProperty("Name").GetValue(item)} - {item.GetType().GetProperty("Age").GetValue(item)}");
        }
    }
}
Alice - 30
Bob - 25

DistinctByメソッド(C# 6.0以降)との違い

DistinctByメソッドは、C# 6.0以降のLINQ拡張メソッドで、特定のプロパティに基づいて重複を削除するために使用されます。

Distinctメソッドがオブジェクト全体を比較するのに対し、DistinctByメソッドは指定したプロパティの値を基準に重複を判断します。

以下は、DistinctByメソッドの使用例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice", Age = 30 },
            new Person { Name = "Bob", Age = 25 },
            new Person { Name = "Alice", Age = 25 }
        };
        
        // DistinctByメソッドを使用して重複を削除
        var distinctPeople = people.DistinctBy(p => p.Name);
        
        foreach (var person in distinctPeople)
        {
            Console.WriteLine($"{person.Name} - {person.Age}");
        }
    }
}
Alice - 30
Bob - 25

このように、DistinctByメソッドを使用することで、特定のプロパティに基づいて簡単に重複を削除することができます。

DistinctメソッドDistinctByメソッドは、使用するシナリオに応じて使い分けることが重要です。

Distinctメソッドの応用例

複数のプロパティを基準にしたDistinctの実装

複数のプロパティを基準に重複を削除する場合、IEqualityComparerを使用してカスタム比較を行うことができます。

以下は、名前と年齢を基準に重複を削除する例です。

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)
    {
        return x.Name == y.Name && x.Age == y.Age; // 名前と年齢が同じなら等しいと判断
    }
    public int GetHashCode(Person obj)
    {
        return obj.Name.GetHashCode() ^ obj.Age.GetHashCode(); // 名前と年齢のハッシュコードを組み合わせる
    }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice", Age = 30 },
            new Person { Name = "Bob", Age = 25 },
            new Person { Name = "Alice", Age = 30 },
            new Person { Name = "Alice", Age = 25 }
        };
        
        // IEqualityComparerを使用して重複を削除
        var distinctPeople = people.Distinct(new PersonComparer());
        
        foreach (var person in distinctPeople)
        {
            Console.WriteLine($"{person.Name} - {person.Age}");
        }
    }
}
Alice - 30
Bob - 25
Alice - 25

LINQクエリ内でのDistinctの使用

LINQクエリ構文を使用してDistinctメソッドを適用することも可能です。

以下は、LINQクエリ内でDistinctを使用する例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<string> fruits = new List<string> { "apple", "banana", "apple", "orange", "banana" };
        
        // LINQクエリ内でDistinctメソッドを使用
        var distinctFruits = (from fruit in fruits
                              select fruit).Distinct();
        
        foreach (var fruit in distinctFruits)
        {
            Console.WriteLine(fruit);
        }
    }
}
apple
banana
orange

Distinctと他のLINQメソッド(Where, Select)との組み合わせ

Distinctメソッドは、他のLINQメソッドと組み合わせて使用することができます。

以下は、WhereメソッドSelectメソッドと組み合わせた例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
        
        // WhereメソッドとSelectメソッドと組み合わせて使用
        var distinctEvenNumbers = numbers
            .Where(n => n % 2 == 0) // 偶数をフィルタリング
            .Distinct() // 重複を削除
            .Select(n => n * 10); // 各要素を10倍にする
        
        foreach (var number in distinctEvenNumbers)
        {
            Console.WriteLine(number);
        }
    }
}
20
40

Distinctを使ったデータのフィルタリング

Distinctメソッドを使用して、特定の条件に基づいてデータをフィルタリングすることができます。

以下は、特定の条件を満たすユニークな値を取得する例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<string> names = new List<string> { "Alice", "Bob", "Alice", "Charlie", "Bob" };
        
        // Distinctメソッドを使用してユニークな名前を取得
        var uniqueNames = names.Distinct().Where(name => name.StartsWith("A")); // "A"で始まる名前
        
        foreach (var name in uniqueNames)
        {
            Console.WriteLine(name);
        }
    }
}
Alice

Distinctを使った重複データの削除と集計

Distinctメソッドを使用して重複データを削除した後、集計を行うことも可能です。

以下は、重複を削除した後にカウントを行う例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
        
        // Distinctメソッドを使用して重複を削除し、カウントを取得
        int distinctCount = numbers.Distinct().Count();
        
        Console.WriteLine($"ユニークな数値の数: {distinctCount}");
    }
}
ユニークな数値の数: 5

このように、Distinctメソッドはさまざまなシナリオで応用可能であり、データの重複を管理するための強力なツールです。

Distinctメソッドの制約と注意点

順序が保証されない点について

Distinctメソッドは、元のコレクションの順序を保持することが一般的ですが、特定の条件下では順序が保証されない場合があります。

特に、IEnumerable<T>を使用している場合、データの順序が変更される可能性があります。

したがって、重複を削除した後に順序を維持したい場合は、元のコレクションをリストに変換するか、OrderByメソッドを使用して明示的に順序を指定する必要があります。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 3, 1, 2, 2, 1, 3 };
        
        // Distinctメソッドを使用して重複を削除
        var distinctNumbers = numbers.Distinct();
        
        foreach (var number in distinctNumbers)
        {
            Console.WriteLine(number); // 順序が保証されない場合がある
        }
    }
}
3
1
2

大量データに対するパフォーマンスの影響

Distinctメソッドは、コレクション内の重複を削除するために、内部でハッシュテーブルを使用します。

このため、大量のデータに対して使用すると、メモリ使用量が増加し、パフォーマンスに影響を与える可能性があります。

特に、非常に大きなコレクションに対してDistinctを適用する場合は、パフォーマンスを考慮し、必要に応じて他の手法(例えば、HashSetを使用するなど)を検討することが重要です。

Distinctメソッドと参照型の挙動

Distinctメソッドは、デフォルトではオブジェクトの参照を基に重複を判断します。

したがって、同じ内容を持つ異なるインスタンスが存在する場合、それらは異なるオブジェクトとして扱われ、重複とは見なされません。

これを回避するためには、IEqualityComparerを実装してカスタム比較を行うか、クラス内でEqualsメソッドGetHashCodeメソッドをオーバーライドする必要があります。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice" },
            new Person { Name = "Alice" } // 異なるインスタンス
        };
        
        // Distinctメソッドを使用して重複を削除
        var distinctPeople = people.Distinct();
        
        foreach (var person in distinctPeople)
        {
            Console.WriteLine(person.Name); // 2つの異なるインスタンスが表示される
        }
    }
}
Alice
Alice

Distinctメソッドとnull値の扱い

Distinctメソッドは、コレクション内にnull値が含まれている場合でも正常に動作します。

null値は他の値と異なるため、重複として扱われません。

ただし、null値が多く含まれる場合、結果に影響を与える可能性があるため、注意が必要です。

特に、カスタム比較を行う場合は、null値の扱いを明示的に考慮する必要があります。

using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
    static void Main()
    {
        List<string> names = new List<string> { "Alice", null, "Bob", null };
        
        // Distinctメソッドを使用して重複を削除
        var distinctNames = names.Distinct();
        
        foreach (var name in distinctNames)
        {
            Console.WriteLine(name ?? "null"); // null値を表示
        }
    }
}
Alice
null
Bob

このように、Distinctメソッドを使用する際には、これらの制約や注意点を理解し、適切に対処することが重要です。

Distinctメソッドの代替手段

HashSetを使った重複削除

HashSetは、コレクション内のユニークな要素を保持するためのデータ構造です。

HashSetを使用することで、重複を簡単に削除することができます。

以下は、HashSetを使用して重複を削除する例です。

using System;
using System.Collections.Generic;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
        
        // HashSetを使用して重複を削除
        HashSet<int> uniqueNumbers = new HashSet<int>(numbers);
        
        foreach (var number in uniqueNumbers)
        {
            Console.WriteLine(number);
        }
    }
}
1
2
3
4
5

GroupByを使った重複削除

GroupByメソッドを使用することで、特定のプロパティに基づいてグループ化し、重複を削除することができます。

以下は、GroupByを使用して重複を削除する例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice" },
            new Person { Name = "Bob" },
            new Person { Name = "Alice" }
        };
        
        // GroupByを使用して重複を削除
        var distinctPeople = people.GroupBy(p => p.Name)
                                   .Select(g => g.First());
        
        foreach (var person in distinctPeople)
        {
            Console.WriteLine(person.Name);
        }
    }
}
Alice
Bob

DistinctByメソッドの利用(C# 6.0以降)

DistinctByメソッドは、特定のプロパティに基づいて重複を削除するための便利なメソッドです。

C# 6.0以降で利用可能で、簡潔に記述できます。

以下は、DistinctByメソッドを使用した例です。

using System;
using System.Collections.Generic;
using System.Linq;
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main()
    {
        List<Person> people = new List<Person>
        {
            new Person { Name = "Alice", Age = 30 },
            new Person { Name = "Bob", Age = 25 },
            new Person { Name = "Alice", Age = 25 }
        };
        
        // DistinctByメソッドを使用して重複を削除
        var distinctPeople = people.DistinctBy(p => p.Name);
        
        foreach (var person in distinctPeople)
        {
            Console.WriteLine($"{person.Name} - {person.Age}");
        }
    }
}
Alice - 30
Bob - 25

自前のループで重複を削除する方法

自前のループを使用して重複を削除することも可能です。

以下は、forループを使用して重複を削除する例です。

using System;
using System.Collections.Generic;
class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
        List<int> uniqueNumbers = new List<int>();
        
        foreach (var number in numbers)
        {
            if (!uniqueNumbers.Contains(number)) // 重複をチェック
            {
                uniqueNumbers.Add(number); // ユニークな数値を追加
            }
        }
        
        foreach (var number in uniqueNumbers)
        {
            Console.WriteLine(number);
        }
    }
}
1
2
3
4
5

このように、Distinctメソッドの代替手段として、HashSetやGroupBy、DistinctByメソッド、自前のループを使用する方法があります。

状況に応じて適切な手法を選択することが重要です。

よくある質問

Distinctメソッドは順序を保持しますか?

Distinctメソッドは、元のコレクションの順序を保持することが一般的です。

ただし、特定の条件下では順序が保証されない場合があります。

特に、IEnumerable<T>を使用している場合、データの順序が変更される可能性があります。

したがって、重複を削除した後に順序を維持したい場合は、元のコレクションをリストに変換するか、OrderByメソッドを使用して明示的に順序を指定する必要があります。

Distinctメソッドはどのように重複を判断しますか?

Distinctメソッドは、デフォルトではオブジェクトの参照を基に重複を判断します。

つまり、同じ内容を持つ異なるインスタンスは重複とは見なされません。

重複を判断するためには、IEqualityComparerを実装してカスタム比較を行うか、クラス内でEqualsメソッドGetHashCodeメソッドをオーバーライドする必要があります。

これにより、特定のプロパティに基づいて重複を削除することが可能になります。

Distinctメソッドはnull値をどのように扱いますか?

Distinctメソッドは、コレクション内にnull値が含まれている場合でも正常に動作します。

null値は他の値と異なるため、重複として扱われません。

したがって、null値が含まれている場合でも、Distinctメソッドはそれを考慮し、ユニークな要素として結果に含めます。

ただし、カスタム比較を行う場合は、null値の扱いを明示的に考慮する必要があります。

まとめ

この記事では、C#のLINQにおけるDistinctメソッドの基本的な使い方やカスタマイズ方法、応用例、制約、代替手段について詳しく解説しました。

Distinctメソッドは、コレクション内の重複を効果的に削除するための強力なツールであり、さまざまなシナリオで活用できます。

これを機に、Distinctメソッドやその代替手段を実際のプロジェクトに取り入れて、データ処理の効率を向上させてみてはいかがでしょうか。

  • URLをコピーしました!
目次から探す