Pertanyaan LINQ's Distinct () pada properti tertentu


Saya bermain dengan LINQ untuk mempelajarinya, tetapi saya tidak tahu cara menggunakan Distinct ketika saya tidak memiliki daftar yang sederhana (daftar bilangan bulat sederhana cukup mudah dilakukan, ini bukan pertanyaan). Apa yang ingin saya gunakan Berbeda pada daftar Objek satu atau lebih properti objek?

Contoh: Jika suatu objek Person, dengan Property Id. Bagaimana saya bisa mendapatkan semua Orang dan menggunakan Distinct pada mereka dengan properti Id dari objek?

Person1: Id=1, Name="Test1"
Person2: Id=1, Name="Test1"
Person3: Id=2, Name="Test2"

Bagaimana saya bisa mendapatkan Person1 dan Person3 saja? Apakah itu mungkin?

Jika tidak mungkin dengan LINQ, apa cara terbaik untuk memiliki daftar Person tergantung pada beberapa propertinya dalam. NET 3.5?


775
2018-01-28 20:45


asal


Jawaban:


EDIT: Ini sekarang bagian dari Lebih dari LINQ.

Yang Anda butuhkan adalah "berbeda-oleh" secara efektif. Saya tidak percaya itu bagian dari LINQ seperti berdiri, meskipun cukup mudah untuk menulis:

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

Jadi untuk menemukan nilai-nilai yang berbeda hanya menggunakan Id properti, Anda bisa menggunakan:

var query = people.DistinctBy(p => p.Id);

Dan untuk menggunakan banyak properti, Anda dapat menggunakan jenis anonim, yang menerapkan kesetaraan dengan tepat:

var query = people.DistinctBy(p => new { p.Id, p.Name });

Belum dicoba, tetapi harus bekerja (dan sekarang setidaknya kompilasi).

Ini mengasumsikan pembanding standar untuk kunci meskipun - jika Anda ingin lulus dalam pembanding kesetaraan, hanya menyebarkannya ke HashSet konstruktor.


925
2018-01-28 21:17



Bagaimana jika saya ingin mendapatkan daftar yang berbeda berdasarkan satu atau lebih properti?

Sederhana! Anda ingin mengelompokkannya dan memilih pemenang dari grup.

List<Person> distinctPeople = allPeople
  .GroupBy(p => p.PersonId)
  .Select(g => g.First())
  .ToList();

Jika Anda ingin mendefinisikan grup di beberapa properti, berikut caranya:

List<Person> distinctPeople = allPeople
  .GroupBy(p => new {p.PersonId, p.FavoriteColor} )
  .Select(g => g.First())
  .ToList();

1456
2018-01-29 14:39



Anda juga bisa menggunakan sintaks query jika Anda ingin melihat semua LINQ-like:

var uniquePeople = from p in people
                   group p by new {p.ID} //or group by new {p.ID, p.Name, p.Whatever}
                   into mygroup
                   select mygroup.FirstOrDefault();

61
2018-03-06 18:28



Saya pikir itu sudah cukup:

list.Select(s => s.MyField).Distinct();

47
2018-01-23 14:54



Menggunakan:

List<Person> pList = new List<Person>();
/* Fill list */

var result = pList.Where(p => p.Name != null).GroupBy(p => p.Id).Select(grp => grp.FirstorDefault());

Itu where membantu Anda memfilter entri (bisa lebih rumit) dan groupby dan select melakukan fungsi yang berbeda.


36
2018-02-14 12:52



Anda dapat melakukan ini dengan standar Linq.ToLookup(). Ini akan membuat kumpulan nilai untuk setiap kunci unik. Cukup pilih item pertama dalam koleksi

Persons.ToLookup(p => p.Id).Select(coll => coll.First());

18
2018-01-20 15:01



Kode berikut secara fungsional setara dengan Jawaban Jon Skeet.

Diuji pada .NET 4.5, seharusnya bekerja pada versi LINQ yang lebih lama.

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
  this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
  HashSet<TKey> seenKeys = new HashSet<TKey>();
  return source.Where(element => seenKeys.Add(keySelector(element)));
}

Secara tidak sengaja, periksa Versi terbaru Jon Skeet dari DistinctBy.cs di Google Code.


15
2018-02-06 19:56



Solusi grup pertama dengan bidang Anda kemudian pilih item firstordefault.

    List<Person> distinctPeople = allPeople
   .GroupBy(p => p.PersonId)
   .Select(g => g.FirstOrDefault())
   .ToList();

14
2017-07-13 08:33



Saya telah menulis artikel yang menjelaskan cara memperpanjang fungsi Distinct sehingga Anda dapat melakukan hal berikut:

var people = new List<Person>();

people.Add(new Person(1, "a", "b"));
people.Add(new Person(2, "c", "d"));
people.Add(new Person(1, "a", "b"));

foreach (var person in people.Distinct(p => p.ID))
    // Do stuff with unique list here.

Inilah artikelnya: Memperluas LINQ - Menentukan Properti di Fungsi Yang Berbeda


10
2018-03-11 12:21



Jika Anda memerlukan metode Berbeda di beberapa properti, Anda dapat memeriksa saya PowerfulExtensions Perpustakaan. Saat ini masih dalam tahap yang sangat muda, tetapi Anda sudah dapat menggunakan metode seperti Distinct, Union, Intersect, Kecuali pada sejumlah properti;

Beginilah cara Anda menggunakannya:

using PowerfulExtensions.Linq;
...
var distinct = myArray.Distinct(x => x.A, x => x.B);

4
2017-08-15 20:20



Anda dapat melakukannya (meskipun tidak secepat kilat) seperti:

people.Where(p => !people.Any(q => (p != q && p.Id == q.Id)));

Yaitu, "pilih semua orang di mana tidak ada orang lain yang berbeda dalam daftar dengan ID yang sama."

Pikiran Anda, dalam contoh Anda, itu hanya akan memilih orang 3. Saya tidak yakin bagaimana membedakan mana yang Anda inginkan, dari dua sebelumnya.


3
2018-01-28 20:47