C# で 1つ以上のプロパティに基づいて個別のリストを取得する

Bilal Shahid 2023年10月12日
  1. GroupBy()Select() を使用して、C# で 1つのプロパティに基づく個別のリストを取得する
  2. GroupBy()Select() を使用して、C# で 1つ以上のプロパティに基づいて個別のリストを取得する
  3. C#DistinctBy を使用して、1つ以上のプロパティに基づいて個別のリストを取得する
C# で 1つ以上のプロパティに基づいて個別のリストを取得する

今日は、異なる値を含む同じプロパティを持つ複数のオブジェクトに対して、LINQ で Distinct() を使用する方法を見ていきます。 まず、以下の例を見てください。

CAR oneCAR twoCAR three の 3つのオブジェクトがあるとします。 CAR oneCAR two のビルド値は 2 ですが、CAR three のビルド値は 4 です。

3つのオブジェクトで Distinct() を呼び出した場合、結果には CAR oneCAR three または CAR twoCAR three のみが含まれる必要があります。これらは両方とも異なる値を含んでいるためです。

この記事では、C# で 1つ以上のプロパティに基づいて個別のリストを取得する方法について説明します。

GroupBy()Select() を使用して、C# で 1つのプロパティに基づく個別のリストを取得する

例を始める前に、まず LINQ ライブラリをコードに追加しましょう。

using SYSTEM.Linq;

次に、次のように CAR クラスを定義しましょう。

class CAR {
  public int id;

  public CAR(int id) {
    this.id = id;
  }
}

次に、Main で、いくつかのオブジェクトを定義しましょう。

CAR one = new CAR(4);
CAR two = new CAR(4);
CAR three = new CAR(6);

List<CAR> cars = new List<CAR>();
cars.Add(one);
cars.Add(two);
cars.Add(three);

また、リストを作成し、3つのオブジェクトを追加しました。 同じプロパティ オブジェクトをトリミングし、そのうちの 1つだけを検討します。

新しいリストを作成し、GroupBY() 句を使用します。 最後に、First() で勝者を選びます。

List<CAR> distinct_c = cars.GroupBy(p => p.id).Select(g => g.First()).ToList();
for (int i = 0; i < distinct_c.Count(); i++) {
  Console.Write(distinct_c[i].id + " ");
}

結果は次のようになります。

4 6

これらは、3つのオブジェクトのそれぞれにある 2つの異なる ID です。 コード全体は次のとおりです。

class pointer {
  class CAR {
    public int id;

    public CAR(int id) {
      this.id = id;
    }
  }

  public static void Main(String[] args) {
    CAR one = new CAR(4);
    CAR two = new CAR(4);
    CAR three = new CAR(6);

    List<CAR> cars = new List<CAR>();
    cars.Add(one);
    cars.Add(two);
    cars.Add(three);

    List<CAR> distinct_c = cars.GroupBy(p => p.id).Select(g => g.First()).ToList();
    for (int i = 0; i < distinct_c.Count(); i++) {
      Console.Write(distinct_c[i].id + " ");
    }
  }
}

上記のコードを namespace に挿入して、それで遊んでください。

GroupBy()Select() を使用して、C# で 1つ以上のプロパティに基づいて個別のリストを取得する

id と一緒に color と呼ばれるプロパティがもう 1つあり、さらに別のプロパティを選択したい場合はどうなるでしょうか。 次のように、color プロパティを CAR クラスに追加しましょう。

class CAR {
  public int id;
  public string color;

  public CAR(int id, string color) {
    this.id = id;
    this.color = color;
  }
}

ただし、上記と同じ構文で関数を呼び出す場合は、次のようにします。

CAR one = new CAR(4, "red");
CAR two = new CAR(4, "blue");
CAR three = new CAR(6, "blue");

List<CAR> cars = new List<CAR>();
cars.Add(one);
cars.Add(two);
cars.Add(three);

List<CAR> distinct_c = cars.GroupBy(p => p.id).Select(g => g.First()).ToList();
for (int i = 0; i < distinct_c.Count(); i++) {
  Console.Write(distinct_c[i].id + " ");
}

出力は 4 6 と同じままです。 p はまだ p => p.id だけでグループ化されているためです。 しかし、 によっても区別したいので、次のようにできます。

List<CAR> distinct_c = cars.GroupBy(p => (p.id, p.color)).Select(g => g.First()).ToList();
for (int i = 0; i < distinct_c.Count(); i++) {
  Console.Write(distinct_c[i].id + " ");
}

では、結果はどうなるでしょうか? 3つのオブジェクトは、IDCAR の組み合わせがすべて異なるためです。 (4, 赤), (4, 青), (6, 青). したがって、すべてのオブジェクトには区別があります。

出力は次のようになります。

4 4 6

完全なコードを以下に示します。

static class pointer {
  class CAR {
    public int id;
    public string color;

    public CAR(int id, string color) {
      this.id = id;
      this.color = color;
    }
  }

  public static void Main(String[] args) {
    CAR one = new CAR(4, "red");
    CAR two = new CAR(4, "blue");
    CAR three = new CAR(6, "blue");

    List<CAR> cars = new List<CAR>();
    cars.Add(one);
    cars.Add(two);
    cars.Add(three);

    List<CAR> distinct_c = cars.GroupBy(p => (p.id, p.color)).Select(g => g.First()).ToList();
    for (int i = 0; i < distinct_c.Count(); i++) {
      Console.Write(distinct_c[i].id + " ");
    }
  }
}

Where 条件を追加して、p.id が null でないかどうかを確認してから呼び出すこともできます。

List<CAR> distinct_c =
    cars.Where(p => p.id != null).GroupBy(p => (p.id, p.color)).Select(g => g.First()).ToList();
for (int i = 0; i < distinct_c.Count(); i++) {
  Console.Write(distinct_c[i].id + " ");
}

結果も同じになります。

C#First() を使用する際の考慮事項

FirstorDefault() で回避できる場合もあります。 First() と同じことを行う別の関数ですが、最初の要素がない場合はデフォルト値を返すだけです。

null 値でのエラーを回避するために使用できます。

C#DistinctBy を使用して、1つ以上のプロパティに基づいて個別のリストを取得する

C# では、個別の値を返すことができる関数を作成できます。

これを行う 1つの方法は、キーと値を取り、それを一緒にマップできるハッシュテーブルを使用することです。 そのような関数を次のように書くことができます:

public static IEnumerable<S> DistinctBy<S, K>(this IEnumerable<S> source, Func<S, K> keySelector) {
  HashSet<K> seen = new HashSet<K>();
  foreach (S element in source) {
    if (seen.Add(keySelector(element))) {
      yield return element;
    }
  }
}

ただし、その前に、MORELINQ をダウンロードしてコード用に構成してください。

var q = cars.DistinctBy(p => p.id);

そして、この変数 q の内容を出力して答えを得ることができます。 完全なコードを以下に示します。

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using MoreLinq;

namespace jinlku_console {
  static class pointer {
    class CAR {
      public int id;
      public string color;

      public CAR(int id, string color) {
        this.id = id;
        this.color = color;
      }
    }

    public static IEnumerable<S> DistinctBy<S, K>(this IEnumerable<S> source,
                                                  Func<S, K> keySelector) {
      HashSet<K> seen = new HashSet<K>();
      foreach (S element in source) {
        if (seen.Add(keySelector(element))) {
          yield return element;
        }
      }
    }

    public static void Main(String[] args) {
      CAR one = new CAR(4, "red");
      CAR two = new CAR(4, "blue");
      CAR three = new CAR(6, "blue");

      List<CAR> cars = new List<CAR>();
      cars.Add(one);
      cars.Add(two);
      cars.Add(three);

      var q = cars.DistinctBy(p => p.id);

      Console.WriteLine(q.Count());

      List<CAR> distinct_c = cars.GroupBy(p => (p.id, p.color)).Select(g => g.First()).ToList();
      for (int i = 0; i < distinct_c.Count(); i++) {
        Console.Write(distinct_c[i].id + " ");
      }
    }
  }
}

C# で 1つ以上のプロパティに基づいて個別のリストを取得する別の簡単なソリューション

同じクエリを記述するもう 1つの方法は、次のとおりです。

var un = from car in cars group car by new(car.id) into mg select mg.First();

ただし、これを使用するには、言語を 9.0 以上に更新する必要がある場合があります。

著者: Bilal Shahid
Bilal Shahid avatar Bilal Shahid avatar

Hello, I am Bilal, a research enthusiast who tends to break and make code from scratch. I dwell deep into the latest issues faced by the developer community and provide answers and different solutions. Apart from that, I am just another normal developer with a laptop, a mug of coffee, some biscuits and a thick spectacle!

GitHub

関連記事 - Csharp LINQ