Entfernen Sie Duplikate in der Liste mit linq


314

Ich habe eine Klasse Itemsmit properties (Id, Name, Code, Price).

Die Liste von Itemswird mit doppelten Elementen gefüllt.

Zum Beispiel:

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

Wie entferne ich die Duplikate in der Liste mit linq?


Ich habe auch eine andere Klasse als Eigenschaft in der Gegenstandsklasse
Prasad

Sie können auch tun var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));. Es sollte kriminell sein, dies zu tun.
Nawfal

Antworten:


394
var distinctItems = items.Distinct();

Erstellen Sie einen benutzerdefinierten Gleichheitsvergleicher, um nur einige der Eigenschaften abzugleichen, z.

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

Dann benutze es so:

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

Hallo Christian, was wird die Änderung im Code sein, wenn ich eine Liste <my_Custom_Class> und eine Liste <string> habe? Meine benutzerdefinierte Klasse enthält verschiedene Elemente, von denen eines die DCN-Nummer und die Liste <string> nur die DCN-Nummer enthält. Ich muss also überprüfen, ob List <Custom_Class> einen beliebigen dcn aus List <string> enthält. Angenommen, List1 = List <Custom_Class> und List2 = List <String>. Wenn Liste1 2000 Elemente enthält und Liste2 40000 Elemente enthält, für die 600 Elemente aus Liste1 in Liste2 vorhanden sind. In diesem Fall benötige ich 1400 als Ausgabeliste als Liste1. Also, was wäre der Ausdruck. Vielen Dank im Voraus

Ein weiterer Fall ist hier, da List1 verschiedene Elemente enthält. Andere Elementwerte können unterschiedlich sein, aber das DCN muss gleich sein. In meinem Fall hat Distinct also nicht die gewünschte Ausgabe abgegeben.

2
Ich finde Vergleichsklassen äußerst nützlich. Sie können andere Logik als einfache Vergleiche von Eigenschaftsnamen ausdrücken. Ich habe letzten Monat einen neuen geschrieben, um etwas zu tun, GroupBywas nicht möglich war.
Christian Hayter

Funktioniert gut und hat mich dazu gebracht, etwas Neues zu lernen und den XoROperator ^in C # zu untersuchen. Hatte in VB.NET über verwendet Xor, musste aber Ihren Code zweimal überprüfen, um zu sehen, was er zuerst war.
Atconway

Dies ist der Fehler, den ich erhalte, wenn ich versuche, Distinct Comparer zu verwenden: "LINQ to Entities erkennt die Methode '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 [nicht DataAccess.HR.Dao.CCS_LOCATION_TBL]) 'Methode, und diese Methode kann nicht in einen Geschäftsausdruck übersetzt werden.
user8128167

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

28
Danke - wollte vermeiden, eine Vergleichsklasse zu schreiben, deshalb bin ich froh, dass dies funktioniert :)
Jen

8
+1 Diese Lösung ermöglicht sogar einen Tie-Breaker: Beseitigen Sie Duplikate mit Kriterien!
Adriano Carneiro

4
Aber ein bisschen Overhead!
Amirhossein Mehrvarzi

1
Aber wie Victor Juri unten vorgeschlagen hat: Verwenden Sie FirstorDefault. Ich kann nicht glauben, dass diese Lösung so einfach sein kann (ohne benutzerdefinierten Gleichheitsvergleich)
CyberHawk

6
Sie können mit mehreren Eigenschaften gruppieren: List <XYZ> MyUniqueList = MyList.GroupBy (x => new {x.Column1, x.Column2}). Select (g => g.First ()). ToList ();
Sumit Joshi

41

Wenn Ihre Distinct-Abfrage durch etwas ausgelöst wird , sollten Sie sich MoreLinq ansehen , den DistinctBy-Operator verwenden und verschiedene Objekte anhand ihrer ID auswählen.

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

1
Es gibt keine DistinctBy () -Methode mit Linq.
Fereydoon Barikzehy

7
@FereydoonBarikzehy Aber er spricht nicht über reinen Linq. In der Post ist Linq zu MoreLinq Projekt ...
Ademar

30

So konnte ich mich mit Linq gruppieren. Ich hoffe es hilft.

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

3
@nawfal, ich schlug FirstOrDefault () anstelle von First () vor
sobelito

23
Wenn ich richtig liege FirstOrDefault, bietet die Verwendung hier keinen Vorteil, wenn das SelectUnmittelbare folgt GroupBy, da es keine Möglichkeit gibt, dass es eine leere Gruppe gibt (die Gruppen wurden nur aus dem Inhalt der Sammlung abgeleitet )
Roy Tinker

17

Verwenden Sie, Distinct()aber denken Sie daran, dass der Standard-Gleichheitsvergleicher zum Vergleichen von Werten verwendet wird. Wenn Sie also etwas darüber hinaus möchten, müssen Sie Ihren eigenen Vergleicher implementieren.

Ein Beispiel finden Sie unter http://msdn.microsoft.com/en-us/library/bb348436.aspx .


Ich sollte beachten, dass der Standardvergleich funktioniert, wenn der Typ der Sammlungselemente einer der Werttypen ist. Aber welcher Standard-Gleichheitsvergleicher wählt csc für Referenztypen aus. Referenztypen müssen über eigene Komparatoren verfügen.
Nuri YILMAZ

16

Sie haben hier drei Möglichkeiten, um doppelte Elemente in Ihrer Liste zu entfernen:

  1. Verwenden Sie einen benutzerdefinierten Gleichheitsvergleich und verwenden Sie ihn dann Distinct(new DistinctItemComparer())wie @Christian Hayter erwähnt.
  2. Verwenden Sie GroupBy, aber bitte beachten GroupBySie , dass Sie nach allen Spalten gruppieren sollten Id, da doppelte Elemente nicht immer entfernt werden, wenn Sie nur danach gruppieren . Betrachten Sie zum Beispiel das folgende Beispiel:

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

    Das Ergebnis für diese Gruppierung ist:

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

    Was falsch ist, weil es {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}als doppelt betrachtet wird. Die richtige Abfrage wäre also:

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

    3.Überschreiben Equalund GetHashCodein Artikelklasse :

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

    Dann können Sie es so verwenden:

    var distinctItems = a.Distinct();

11

Eine universelle Erweiterungsmethode:

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

Anwendungsbeispiel:

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

Sehr sauberer Ansatz
Steven Ryssaert

4

Probieren Sie diese Erweiterungsmethode aus. Hoffentlich könnte das helfen.

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

Verwendungszweck:

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

Eine weitere Problemumgehung, nicht schön kaufen praktikabel.

Ich habe eine XML-Datei mit einem Element namens "MEMDES" mit zwei Attributen als "GRADE" und "SPD", um die RAM-Modulinformationen aufzuzeichnen. Es gibt viele doppelte Gegenstände in der SPD.

Hier ist der Code, mit dem ich die doppelten Elemente entferne:

        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

Wenn Sie IEqualityComparer nicht schreiben möchten, können Sie Folgendes versuchen.

 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; }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.