ลบรายการที่ซ้ำในรายการโดยใช้ linq


314

ฉันได้เรียนกับItemsproperties (Id, Name, Code, Price)

รายการของItemsถูกบรรจุด้วยรายการที่ซ้ำกัน

ตัวอย่างเช่น:

1         Item1       IT00001        $100
2         Item2       IT00002        $200
3         Item3       IT00003        $150
1         Item1       IT00001        $100
3         Item3       IT00003        $150

วิธีการลบรายการที่ซ้ำกันในรายการโดยใช้ linq


ฉันได้เรียนอื่นเป็นคุณสมบัติในรายการชั้นยัง
ปรา

var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));นอกจากนี้คุณยังสามารถทำ มันควรจะเป็นความผิดทางอาญาที่จะทำเช่นนั้น ..
Nawfal

คำตอบ:


394
var distinctItems = items.Distinct();

หากต้องการจับคู่กับคุณสมบัติบางอย่างเท่านั้นให้สร้างเครื่องมือเปรียบเทียบความเท่าเทียมกันที่กำหนดเองเช่น:

class DistinctItemComparer : IEqualityComparer<Item> {

    public bool Equals(Item x, Item y) {
        return x.Id == y.Id &&
            x.Name == y.Name &&
            x.Code == y.Code &&
            x.Price == y.Price;
    }

    public int GetHashCode(Item obj) {
        return obj.Id.GetHashCode() ^
            obj.Name.GetHashCode() ^
            obj.Code.GetHashCode() ^
            obj.Price.GetHashCode();
    }
}

จากนั้นใช้แบบนี้:

var distinctItems = items.Distinct(new DistinctItemComparer());

สวัสดีคริสเตียนจะมีการเปลี่ยนแปลงรหัสอย่างไรหากฉันมีรายการ <my_Custom_Class> และรายการ <string> คลาสที่กำหนดเองของฉันมีรายการต่าง ๆ ที่หนึ่งคือหมายเลข DCN และรายการ <string> มีหมายเลข DCN เท่านั้น ดังนั้นฉันต้องตรวจสอบ List <Custom_Class> มี dcn จาก List <string> ตัวอย่างเช่นสมมติว่า List1 = List <Custom_Class> และ List2 = List <String> ถ้า List1 มี 2,000 รายการและ list2 มี 40000 รายการซึ่งมี 600 รายการจาก List1 อยู่ใน List2 ดังนั้นในกรณีนี้ฉันต้องการ 1,400 รายการของฉันเป็น list1 ดังนั้นสิ่งที่จะแสดงออก ขอบคุณล่วงหน้า

อีกกรณีหนึ่งอยู่ที่นี่เนื่องจาก List1 มีไอเท็มต่าง ๆ ค่าไอเท็มอื่นอาจแตกต่างกัน แต่ DCN ต้องเหมือนกัน ดังนั้นในกรณีของฉัน Distinct ล้มเหลวที่จะให้ออกที่ต้องการ

2
ฉันพบว่าคลาสตัวเปรียบเทียบมีประโยชน์อย่างยิ่ง พวกเขาสามารถแสดงเหตุผลอื่น ๆ นอกเหนือจากการเปรียบเทียบชื่อคุณสมบัติง่าย ๆ ฉันเขียนใหม่เมื่อเดือนที่แล้วเพื่อทำสิ่งที่GroupByไม่สามารถทำได้
Christian Hayter

ทำงานได้ดีและให้ฉันเรียนรู้สิ่งใหม่และตรวจสอบXoRผู้ควบคุม^ใน C # มีการใช้ใน VB.NET ผ่านXorแต่ต้องทำสองครั้งเพื่อรหัสของคุณเพื่อดูว่ามันคืออะไรในตอนแรก
atconway

นี่คือข้อผิดพลาดที่ฉันได้รับเมื่อฉันพยายามใช้ Distinct Comparer: "LINQ to Entities ไม่รู้จักวิธีการ 'System.Linq.IQueryable 1[DataAccess.HR.Dao.CCS_LOCATION_TBL] Distinct[CCS_LOCATION_TBL](System.Linq.IQueryable1 [DataAccess.HR.Dao.CCS_LOCATION_TBL], System.Collections.Generic.IEqualityComparer`1 [ DataAccess.HR.Dao.CCS_LOCATION_TBL]) 'วิธีการและวิธีนี้ไม่สามารถแปลเป็นนิพจน์ร้านค้าได้
user8128167

600
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());

28
ขอบคุณ - ถูกมองที่จะหลีกเลี่ยงการเขียนระดับ Comparer ดังนั้นฉันดีใจที่งานนี้ :)
เจน

8
+1 โซลูชันนี้ยังช่วยให้ tie-breaker: กำจัดรายการที่ซ้ำกันด้วยเกณฑ์!
Adriano Carneiro

4
แต่ค่าใช้จ่ายเล็กน้อย!
Amirhossein Mehrvarzi

1
แต่ตามที่ Victor Juri แนะนำด้านล่าง: ใช้ FirstorDefault ไม่สามารถเชื่อได้ว่าวิธีแก้ปัญหานั้นง่ายมาก (โดยไม่ต้องมีการเปรียบเทียบความเท่าเทียมกันที่กำหนดเอง)
CyberHawk

6
คุณสามารถจัดกลุ่มที่มีคุณสมบัติหลายรายการ: รายการ <XYZ> MyUniqueList = MyList.GroupBy (x => ใหม่ {x.Column1, x.Column2}) เลือก (g => g.First ()) ToList ();
Sumit Joshi

41

หากมีบางสิ่งที่สลัดข้อความค้นหาที่แตกต่างของคุณคุณอาจต้องการดูMoreLinqและใช้ตัวดำเนินการ DistinctBy และเลือกวัตถุที่แตกต่างกันตามรหัส

var distinct = items.DistinctBy( i => i.Id );

1
ไม่มีวิธี DistinctBy () กับ Linq
Fereydoon Barikzehy

7
@FereydoonBarikzehy แต่เขาไม่ได้พูดถึง Linq บริสุทธิ์ ในโพสต์คือ linq ถึงโครงการ MoreLinq ...
Ademar

30

นี่คือวิธีที่ฉันสามารถจัดกลุ่มโดยกับ Linq หวังว่ามันจะช่วย

var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());

3
@nawfal ฉันแนะนำ FirstOrDefault () แทน First ()
sobelito

23
หากฉันถูกต้องการใช้FirstOrDefaultที่นี่จะไม่เป็นประโยชน์หากสิ่งSelectต่อไปนี้ทันทีGroupByเนื่องจากไม่มีความเป็นไปได้ที่จะมีกลุ่มว่างเปล่า (กลุ่มเพิ่งมาจากเนื้อหาของคอลเลกชัน)
Roy Tinker

17

ใช้Distinct()แต่จำไว้ว่ามันจะใช้ Comparer เท่าเทียมกันเริ่มต้นเพื่อเปรียบเทียบค่าดังนั้นหากคุณต้องการอะไรนอกเหนือจากที่คุณต้องใช้เครื่องมือเปรียบเทียบของคุณเอง

โปรดดูhttp://msdn.microsoft.com/en-us/library/bb348436.aspxสำหรับตัวอย่าง


ฉันควรสังเกตว่าตัวเปรียบเทียบเริ่มต้นทำงานได้ถ้าประเภทสมาชิกคอลเลกชันเป็นหนึ่งในประเภทค่า แต่ตัวเปรียบเทียบความเท่าเทียมกันเริ่มต้นใดที่เลือกโดย csc สำหรับประเภทการอ้างอิง ประเภทการอ้างอิงจะต้องมีเครื่องมือเปรียบเทียบของตัวเอง
นูริ YILMAZ

16

คุณมีสามตัวเลือกที่นี่เพื่อลบรายการที่ซ้ำกันในรายการของคุณ:

  1. ใช้ aa เครื่องมือเปรียบเทียบความเท่าเทียมกันที่กำหนดเองและใช้Distinct(new DistinctItemComparer())เป็น@Christian Hayter ที่กล่าวถึง
  2. ใช้GroupByแต่โปรดทราบGroupByว่าคุณควรจัดกลุ่มตามคอลัมน์ทั้งหมดเพราะถ้าคุณเพียงแค่จัดกลุ่มIdมันจะไม่ลบรายการที่ซ้ำกันเสมอไป ตัวอย่างเช่นพิจารณาตัวอย่างต่อไปนี้:

    List<Item> a = new List<Item>
    {
        new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
        new Item {Id = 2, Name = "Item2", Code = "IT00002", Price = 200},
        new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
        new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
        new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
        new Item {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}
    };
    var distinctItems = a.GroupBy(x => x.Id).Select(y => y.First());

    ผลลัพธ์สำหรับการจัดกลุ่มนี้จะเป็น:

    {Id = 1, Name = "Item1", Code = "IT00001", Price = 100}
    {Id = 2, Name = "Item2", Code = "IT00002", Price = 200}
    {Id = 3, Name = "Item3", Code = "IT00003", Price = 150}

    ซึ่งไม่ถูกต้องเพราะถือว่า{Id = 3, Name = "Item3", Code = "IT00004", Price = 250}ซ้ำกัน ดังนั้นแบบสอบถามที่ถูกต้องจะเป็น:

    var distinctItems = a.GroupBy(c => new { c.Id , c.Name , c.Code , c.Price})
                         .Select(c => c.First()).ToList();

    3.Override EqualและGetHashCodeใน class item:

    public class Item
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Code { get; set; }
        public int Price { get; set; }
    
        public override bool Equals(object obj)
        {
            if (!(obj is Item))
                return false;
            Item p = (Item)obj;
            return (p.Id == Id && p.Name == Name && p.Code == Code && p.Price == Price);
        }
        public override int GetHashCode()
        {
            return String.Format("{0}|{1}|{2}|{3}", Id, Name, Code, Price).GetHashCode();
        }
    }

    จากนั้นคุณสามารถใช้สิ่งนี้:

    var distinctItems = a.Distinct();

11

วิธีการขยายสากล:

public static class EnumerableExtensions
{
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector)
    {
        return enumerable.GroupBy(keySelector).Select(grp => grp.First());
    }
}

ตัวอย่างการใช้งาน:

var lstDst = lst.DistinctBy(item => item.Key);

แนวทางสะอาดมาก
Steven Ryssaert

4

ลองใช้วิธีการขยายนี้ หวังว่านี่จะช่วยได้

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

การใช้งาน:

var outputList = sourceList.DistinctBy(x => x.TargetProperty);

3
List<Employee> employees = new List<Employee>()
{
    new Employee{Id =1,Name="AAAAA"}
    , new Employee{Id =2,Name="BBBBB"}
    , new Employee{Id =3,Name="AAAAA"}
    , new Employee{Id =4,Name="CCCCC"}
    , new Employee{Id =5,Name="AAAAA"}
};

List<Employee> duplicateEmployees = employees.Except(employees.GroupBy(i => i.Name)
                                             .Select(ss => ss.FirstOrDefault()))
                                            .ToList();

0

วิธีแก้ปัญหาอื่นไม่สวยงามซื้อได้ใช้การได้

ฉันมีไฟล์ XML พร้อมองค์ประกอบที่เรียกว่า "MEMDES" ที่มีสองแอตทริบิวต์เป็น "GRADE" และ "SPD" เพื่อบันทึกข้อมูลโมดูล RAM มีรายการที่ซ้ำซ้อนจำนวนมากใน SPD

ดังนั้นนี่คือรหัสที่ฉันใช้เพื่อลบรายการที่ซ้ำกัน:

        IEnumerable<XElement> MList =
            from RAMList in PREF.Descendants("MEMDES")
            where (string)RAMList.Attribute("GRADE") == "DDR4"
            select RAMList;

        List<string> sellist = new List<string>();

        foreach (var MEMList in MList)
        {
            sellist.Add((string)MEMList.Attribute("SPD").Value);
        }

        foreach (string slist in sellist.Distinct())
        {
            comboBox1.Items.Add(slist);
        }

-1

เมื่อคุณไม่ต้องการเขียน IEqualityComparer คุณสามารถลองทำสิ่งต่อไปนี้

 class Program
{

    private static void Main(string[] args)
    {

        var items = new List<Item>();
        items.Add(new Item {Id = 1, Name = "Item1"});
        items.Add(new Item {Id = 2, Name = "Item2"});
        items.Add(new Item {Id = 3, Name = "Item3"});

        //Duplicate item
        items.Add(new Item {Id = 4, Name = "Item4"});
        //Duplicate item
        items.Add(new Item {Id = 2, Name = "Item2"});

        items.Add(new Item {Id = 3, Name = "Item3"});

        var res = items.Select(i => new {i.Id, i.Name})
            .Distinct().Select(x => new Item {Id = x.Id, Name = x.Name}).ToList();

        // now res contains distinct records
    }



}


public class Item
{
    public int Id { get; set; }

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