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?

Prasad
quelle
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());
Christian Hayter
quelle
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());
Freddy
quelle
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 );
Tvanfosson
quelle
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());
Victor Juri
quelle
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 .

Brian Rasmussen
quelle
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();
Salah Akbari
quelle
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);
TOL
quelle
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);
Kent Aguilar
quelle
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();
Arun Kumar
quelle
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);
        }
Rex Hsu
quelle
-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; }
}
Kundan Bhati
quelle