query linq untuk mengembalikan nilai bidang yang berbeda dari daftar objek

91
class obj
{
    int typeId; //10 types  0-9 
    string uniqueString; //this is unique
}

Asumsikan ada daftar dengan 100 elemen obj, tetapi hanya 10 typeID unik.
Apakah mungkin untuk menulis query LINQ mengembalikan 10 int unik dari daftar obj?

patrick
sumber
Pertanyaan ini sangat mudah dan tidak cocok untuk dijadikan hadiah.
Thinker

Jawaban:

157
objList.Select(o=>o.typeId).Distinct()
Arsen Mkrtchyan
sumber
2
Bentuk kedua tampaknya menggunakan kelebihan Distinct yang tidak ada sejauh yang saya sadari.
Jon Skeet
Ups, hapus yang kedua, terima kasih @Jon Skeet, itu bisa dilakukan dengan IEqualityComparer
Arsen Mkrtchyan
3
Seperti yang disebutkan Jon Skeet, ini hanya akan mengembalikan properti yang ditentukan dalam Select.
Peet vd Westhuizen
59

Dengan asumsi Anda menginginkan objek penuh, tetapi hanya ingin berurusan dengan perbedaan typeID, tidak ada yang dibangun ke dalam LINQ untuk membuatnya mudah. (Jika Anda hanya menginginkan typeIDnilainya, mudah - proyeksikan itu dengan Selectdan kemudian gunakan Distinctpanggilan normal .)

Di MoreLINQ kami memiliki DistinctByoperator yang dapat Anda gunakan:

var distinct = list.DistinctBy(x => x.typeID);

Ini hanya berfungsi untuk LINQ ke Objek.

Anda dapat menggunakan pengelompokan atau pencarian, ini hanya agak mengganggu dan tidak efisien:

var distinct = list.GroupBy(x => x.typeID, (key, group) => group.First());
Jon Skeet
sumber
Agar DistinctBy muncul, Anda perlu menambahkan namespace Microsoft.Ajax.Utilities
Shiljo Paulson
1
@Shil: Tidak, saya sedang menulis tentang DistinctBy di MoreLINQ. Tidak ada hubungannya dengan Microsoft.Ajax.Utilities.
Jon Skeet
Sekarang saya dapat melihat ada kelebihan Distinct di LINQ yang mengambil IEqualityComparer sebagai parameter dan mengembalikan daftar objek yang berbeda tergantung pada implementasi metode di IEqualityComparer
Dipendu Paul
1
@DipenduPaul: Ya, tapi itu tetap berarti membuat pembanding kesetaraan untuk properti tertentu, yang mengganggu dan membuatnya lebih sulit untuk dibaca. Jika Anda dapat mengambil ketergantungan MoreLINQ, saya pikir itu lebih bersih.
Jon Skeet
26

Jika hanya ingin menggunakan Linq murni, Anda dapat menggunakan groupby:

List<obj> distinct =
  objs.GroupBy(car => car.typeID).Select(g => g.First()).ToList();

Jika Anda ingin metode digunakan di seluruh aplikasi, mirip dengan yang dilakukan MoreLinq :

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.Contains(keySelector(element)))
        {
            seenKeys.Add(keySelector(element));
            yield return element;
        }
    }
}

Dengan menggunakan metode ini untuk menemukan nilai yang berbeda hanya dengan menggunakan properti Id, Anda dapat menggunakan:

var query = objs.DistinctBy(p => p.TypeId);

Anda dapat menggunakan beberapa properti:

var query = objs.DistinctBy(p => new { p.TypeId, p.Name });
JulioCT
sumber
Terima kasih untuk bagian multi properti !
Nae
7

Tentu, gunakan Enumerable.Distinct .

Diberikan koleksi obj(mis. foo), Anda akan melakukan sesuatu seperti ini:

var distinctTypeIDs = foo.Select(x => x.typeID).Distinct();
Donat
sumber
5

Saya pikir inilah yang Anda cari:

    var objs= (from c in List_Objects 
orderby c.TypeID  select c).GroupBy(g=>g.TypeID).Select(x=>x.FirstOrDefault());      

Mirip dengan Returning a Distinct IQuerizable with LINQ?

Pengukur
sumber
.Firstbaik-baik saja, karena Anda tidak akan memiliki grup jika tidak ada sesuatu di dalamnya.
mqp
1
Anda dapat menggunakan overload alternatif GroupByuntuk membuat ini lebih sederhana juga - lihat jawaban saya sebagai contoh.
Jon Skeet
3

Jika hanya ingin menggunakan Linq, Anda dapat mengganti metode Equals dan GetHashCode .

Kelas produk :

public class Product
{
    public string ProductName { get; set; }
    public int Id { get; set; }


    public override bool Equals(object obj)
    {
        if (!(obj is Product))
        {
            return false;
        }

        var other = (Product)obj;
        return Id == other.Id;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

Metode Utama :

static void Main(string[] args)
    {

        var products = new List<Product>
        {
            new Product{ ProductName="Product 1",Id = 1},
            new Product{ ProductName="Product 2",Id = 2},
            new Product{ ProductName="Product 4",Id = 5},
            new Product{ ProductName="Product 3",Id = 3},
            new Product{ ProductName="Product 4",Id = 4},
            new Product{ ProductName="Product 6",Id = 4},
            new Product{ ProductName="Product 6",Id = 4},
        };

        var itemsDistinctByProductName = products.Distinct().ToList();

        foreach (var product in itemsDistinctByProductName)
        {
            Console.WriteLine($"Product Id : {product.Id} ProductName : {product.ProductName} ");
        }

        Console.ReadKey();
    }
Reza Jenabi
sumber
1

Saya ingin mengikat data tertentu ke dropdown dan itu harus berbeda. Saya melakukan hal berikut:

List<ClassDetails> classDetails;
List<string> classDetailsData = classDetails.Select(dt => dt.Data).Distinct.ToList();
ddlData.DataSource = classDetailsData;
ddlData.Databind();

Lihat apakah itu membantu

Charmy Vora
sumber