linq query เพื่อส่งคืนค่าฟิลด์ที่แตกต่างจากรายการวัตถุ


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

สมมติว่ามีรายการที่มีองค์ประกอบของ obj 100 รายการ แต่มีเฉพาะ typeID 10 รายการเท่านั้น
เป็นไปได้ไหมที่จะเขียนแบบสอบถาม LINQ ส่งคืน 10 ints ที่ไม่ซ้ำกันจากรายการ objs


คำถามนี้ง่ายมากและไม่เหมาะกับค่าหัว
นักคิด

คำตอบ:


157
objList.Select(o=>o.typeId).Distinct()

2
รูปแบบที่สองดูเหมือนจะใช้ความแตกต่างที่มากเกินไปซึ่งไม่มีอยู่เท่าที่ฉันทราบ
Jon Skeet

อ๊ะลบอันที่สองออกขอบคุณ @Jon Skeet ที่สามารถทำได้ด้วย IEqualityComparer
Arsen Mkrtchyan

3
ดังที่ Jon Skeet กล่าวไว้สิ่งนี้จะส่งคืนเฉพาะคุณสมบัติที่ระบุใน Select
Peet vd Westhuizen

59

สมมติว่าคุณต้องการวัตถุเต็มรูปแบบ แต่ต้องการจัดการกับความแตกต่างเท่านั้นโดยtypeIDไม่มีอะไรใน LINQ เพื่อให้ง่ายขึ้น (หากคุณต้องการเพียงแค่typeIDค่ามันเป็นเรื่องง่าย - ฉายภาพนั้นด้วยSelectการDistinctโทรปกติ)

ในMoreLINQเรามีตัวDistinctByดำเนินการที่คุณสามารถใช้ได้:

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

สิ่งนี้ใช้ได้กับ LINQ ถึง Objects เท่านั้น

คุณสามารถใช้การจัดกลุ่มหรือการค้นหามันค่อนข้างน่ารำคาญและไม่มีประสิทธิภาพ:

var distinct = list.GroupBy(x => x.typeID, (key, group) => group.First());

เพื่อให้มี DistinctBy ปรากฏขึ้นคุณต้องเพิ่มเนมสเปซ Microsoft.Ajax.Utilities
Shiljo Paulson

1
@ ชิล: ไม่ฉันเขียนเกี่ยวกับ DistinctBy ใน MoreLINQ ไม่เกี่ยวอะไรกับ Microsoft.Ajax.Utilities
Jon Skeet

ตอนนี้ฉันเห็นว่ามีความแตกต่างมากเกินไปใน LINQ ซึ่งใช้ IEqualityComparer เป็นพารามิเตอร์และส่งคืนรายการของวัตถุที่แตกต่างกันขึ้นอยู่กับการใช้งานวิธีการใน IEqualityComparer
Dipendu Paul

1
@DipenduPaul: ใช่ แต่นั่นยังหมายถึงการสร้างตัวเปรียบเทียบความเท่าเทียมกันสำหรับคุณสมบัติที่กำหนดซึ่งน่ารำคาญและทำให้อ่านยากขึ้น ถ้าคุณสามารถพึ่งพา MoreLINQ ฉันคิดว่ามันจะสะอาดกว่า
Jon Skeet

26

หากต้องการเพียงแค่ผู้ใช้ Linq บริสุทธิ์คุณสามารถใช้ groupby:

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

หากคุณต้องการใช้วิธีการทั้งหมดในแอปคล้ายกับที่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;
        }
    }
}

เมื่อใช้วิธีนี้เพื่อค้นหาค่าที่แตกต่างโดยใช้เพียงคุณสมบัติ Id คุณสามารถใช้:

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

คุณสามารถใช้คุณสมบัติหลายอย่าง:

var query = objs.DistinctBy(p => new { p.TypeId, p.Name });

ขอบคุณสำหรับคุณสมบัติที่หลากหลาย !
Nae

7

Enumerable.Distinctแน่นอนว่าการใช้งาน

เมื่อพิจารณาจากคอลเล็กชันobj(เช่นfoo) คุณจะต้องทำสิ่งนี้:

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

5

ฉันคิดว่านี่คือสิ่งที่คุณกำลังมองหา:

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

คล้ายกับการกลับมาของ IQueryable ที่แตกต่างด้วย LINQ หรือไม่?


.Firstเป็นเรื่องปกติเนื่องจากคุณจะไม่มีกลุ่มถ้าไม่มีอะไรอยู่ในนั้น
mqp

1
คุณสามารถใช้โอเวอร์โหลดทางเลือกอื่นGroupByเพื่อทำให้ง่ายขึ้นได้เช่นกัน - ดูคำตอบของฉันสำหรับตัวอย่าง
Jon Skeet

3

หากต้องการใช้ Linq คุณสามารถแทนที่เมธอดEqualsและGetHashCodeได้

ชั้นผลิตภัณฑ์ :

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();
    }
}

วิธีหลัก :

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();
    }

1

ฉันต้องการผูกข้อมูลเฉพาะกับดรอปดาวน์และควรมีความแตกต่างกัน ฉันทำสิ่งต่อไปนี้:

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

ดูว่าช่วยได้ไหม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.