C # LINQ findet Duplikate in der Liste


333

List<int>Wie kann ich mit LINQ aus a eine Liste abrufen, die mehr als einmal wiederholte Einträge und deren Werte enthält?

Antworten:


566

Der einfachste Weg, um das Problem zu lösen, besteht darin, die Elemente anhand ihres Werts zu gruppieren und dann einen Vertreter der Gruppe auszuwählen, wenn die Gruppe mehr als ein Element enthält. In LINQ bedeutet dies:

var query = lst.GroupBy(x => x)
              .Where(g => g.Count() > 1)
              .Select(y => y.Key)
              .ToList();

Wenn Sie wissen möchten, wie oft die Elemente wiederholt werden, können Sie Folgendes verwenden:

var query = lst.GroupBy(x => x)
              .Where(g => g.Count() > 1)
              .Select(y => new { Element = y.Key, Counter = y.Count() })
              .ToList();

Dies gibt Listeinen anonymen Typ zurück, und jedes Element verfügt über die Eigenschaften Elementund Counter, um die benötigten Informationen abzurufen.

Und wenn es sich um ein Wörterbuch handelt, das Sie suchen, können Sie es verwenden

var query = lst.GroupBy(x => x)
              .Where(g => g.Count() > 1)
              .ToDictionary(x => x.Key, y => y.Count());

Dies gibt ein Wörterbuch mit Ihrem Element als Schlüssel und der Häufigkeit zurück, mit der es als Wert wiederholt wird.


Nun nur ein Wunder, nehmen wir an, dass duplizierte int in n int-Arrays verteilt werden. Ich verwende das Wörterbuch und die for-Schleife, um zu verstehen, welches Array ein Duplikat enthält, und entferne es gemäß einer Verteilungslogik. Gibt es einen schnellsten Weg (linq wundert sich), um dieses Ergebnis erzielen? Vielen Dank im Voraus für das Interesse.
Mirko Arcese

Ich mache so etwas: code for (int i = 0; i <duplicates.Count; i ++) {int duplicate = duplicates [i]; duplicatesLocation.Add (duplizieren, neue Liste <int> ()); für (int k = 0; k <HitsList.Length; k ++) {if (HitsList [k]. Enthält (Duplikat)) {DuplikateLocation.ElementAt (i) .Value.Add (k); }} // entferne Duplikate nach einigen Regeln. }code
Mirko Arcese

Wenn Sie Duplikate in einer Liste von Arrays finden möchten, werfen
Sie

Ich suche nach Duplikaten in einer Reihe von Listen, habe aber nicht verstanden, wie Selectmany mir helfen kann, es herauszufinden
Mirko Arcese

1
Um zu überprüfen, ob eine Sammlung mehr als ein Element enthält, ist es effizienter, Skip (1) .Any () anstelle von Count () zu verwenden. Stellen Sie sich eine Sammlung mit 1000 Elementen vor. Überspringen (1). Jeder () erkennt, dass mehr als 1 vorhanden ist, sobald das 2. Element gefunden wurde. Für die Verwendung von Count () muss auf die gesamte Sammlung zugegriffen werden.
Harald Coppoolse

133

Finden Sie heraus, ob eine Aufzählung ein Duplikat enthält :

var anyDuplicate = enumerable.GroupBy(x => x.Key).Any(g => g.Count() > 1);

Finden Sie heraus, ob alle Werte in einer Aufzählung eindeutig sind :

var allUnique = enumerable.GroupBy(x => x.Key).All(g => g.Count() == 1);

Gibt es eine Möglichkeit, dass dies nicht immer boolesche Gegensätze sind? anyDuplicate ==! allUnique in allen Fällen.
Garr Godfrey

1
@ GarrGodfrey Sie sind immer boolesche Gegensätze
Caltor

21

Ein anderer Weg ist HashSet:

var hash = new HashSet<int>();
var duplicates = list.Where(i => !hash.Add(i));

Wenn Sie eindeutige Werte in Ihrer Duplikatliste haben möchten:

var myhash = new HashSet<int>();
var mylist = new List<int>(){1,1,2,2,3,3,3,4,4,4};
var duplicates = mylist.Where(item => !myhash.Add(item)).Distinct().ToList();

Hier ist die gleiche Lösung wie bei einer generischen Erweiterungsmethode:

public static class Extensions
{
  public static IEnumerable<TSource> GetDuplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IEqualityComparer<TKey> comparer)
  {
    var hash = new HashSet<TKey>(comparer);
    return source.Where(item => !hash.Add(selector(item))).ToList();
  }

  public static IEnumerable<TSource> GetDuplicates<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
  {
    return source.GetDuplicates(x => x, comparer);      
  }

  public static IEnumerable<TSource> GetDuplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
  {
    return source.GetDuplicates(selector, null);
  }

  public static IEnumerable<TSource> GetDuplicates<TSource>(this IEnumerable<TSource> source)
  {
    return source.GetDuplicates(x => x, null);
  }
}

Dies funktioniert nicht wie erwartet. Unter Verwendung List<int> { 1, 2, 3, 4, 5, 2 }als Quelle ist das Ergebnis ein IEnumerable<int>Element mit dem Wert 1(wobei der korrekte doppelte Wert 2 ist)
BCA

@BCA gestern, ich denke du liegst falsch. Schauen Sie sich dieses Beispiel an: dotnetfiddle.net/GUnhUl
HuBeZa

Ihre Geige druckt das richtige Ergebnis aus. Ich habe jedoch die Zeile Console.WriteLine("Count: {0}", duplicates.Count());direkt darunter hinzugefügt und sie wird gedruckt 6. Sofern mir nichts über die Anforderungen für diese Funktion fehlt, sollte die resultierende Sammlung nur 1 Element enthalten.
BCA

@BCA gestern ist es ein Fehler, der durch die verzögerte Ausführung von LINQ verursacht wurde. Ich habe hinzugefügt ToList, um das Problem zu beheben, aber es bedeutet, dass die Methode ausgeführt wird, sobald sie aufgerufen wird, und nicht, wenn Sie die Ergebnisse durchlaufen.
HuBeZa

var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));führt zu einer Liste, die alle Vorkommen von Duplikaten enthält. Wenn Sie also vier Vorkommen von 2 in Ihrer Liste haben, enthält Ihre doppelte Liste drei Vorkommen von 2, da nur eine der 2 zum HashSet hinzugefügt werden kann. Wenn Sie möchten, dass Ihre Liste eindeutige Werte für jedes Duplikat enthält, verwenden Sie stattdessen diesen Code:var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
solid_luffy

10

Du kannst das:

var list = new[] {1,2,3,1,4,2};
var duplicateItems = list.Duplicates();

Mit diesen Erweiterungsmethoden:

public static class Extensions
{
    public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
    {
        var grouped = source.GroupBy(selector);
        var moreThan1 = grouped.Where(i => i.IsMultiple());
        return moreThan1.SelectMany(i => i);
    }

    public static IEnumerable<TSource> Duplicates<TSource, TKey>(this IEnumerable<TSource> source)
    {
        return source.Duplicates(i => i);
    }

    public static bool IsMultiple<T>(this IEnumerable<T> source)
    {
        var enumerator = source.GetEnumerator();
        return enumerator.MoveNext() && enumerator.MoveNext();
    }
}

Die Verwendung von IsMultiple () in der Duplicates-Methode ist schneller als Count (), da hierdurch nicht die gesamte Auflistung iteriert wird.


Wenn man sich die aussehen Referenzquelle für die Gruppierung können Sie sehen , dass Count() ist vorbestellt berechnet und Ihre Lösung ist wahrscheinlich langsamer.
Johnbot

@ Johnbot. Sie haben Recht, in diesem Fall ist es schneller und die Implementierung wird sich wahrscheinlich nie ändern ... aber es hängt von einem Implementierungsdetail der Implementierungsklasse hinter IGrouping ab. Mit meiner Implementierung wissen Sie, dass sie niemals die gesamte Sammlung durchlaufen wird.
Alex Siepman

Das Zählen von [ Count()] unterscheidet sich also grundlegend vom Iterieren der gesamten Liste. Count()ist vorberechnet, die gesamte Liste jedoch nicht.
Jogi

@ Rehan Khan: Ich verstehe den Unterschied zwischen Count () und Count () nicht
Alex Siepman

2
@RehanKhan: IsMultiple führt KEIN Count () durch, es stoppt sofort nach 2 Elementen. Genau wie Take (2) .Count> = 2;
Alex Siepman

6

Ich habe eine Erweiterung erstellt, um darauf zu antworten, dass Sie sie in Ihre Projekte aufnehmen können. Ich denke, dies ist der häufigste Fall, wenn Sie in List oder Linq nach Duplikaten suchen.

Beispiel:

//Dummy class to compare in list
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public Person(int id, string name, string surname)
    {
        this.Id = id;
        this.Name = name;
        this.Surname = surname;
    }
}


//The extention static class
public static class Extention
{
    public static IEnumerable<T> getMoreThanOnceRepeated<T>(this IEnumerable<T> extList, Func<T, object> groupProps) where T : class
    { //Return only the second and next reptition
        return extList
            .GroupBy(groupProps)
            .SelectMany(z => z.Skip(1)); //Skip the first occur and return all the others that repeats
    }
    public static IEnumerable<T> getAllRepeated<T>(this IEnumerable<T> extList, Func<T, object> groupProps) where T : class
    {
        //Get All the lines that has repeating
        return extList
            .GroupBy(groupProps)
            .Where(z => z.Count() > 1) //Filter only the distinct one
            .SelectMany(z => z);//All in where has to be retuned
    }
}

//how to use it:
void DuplicateExample()
{
    //Populate List
    List<Person> PersonsLst = new List<Person>(){
    new Person(1,"Ricardo","Figueiredo"), //fist Duplicate to the example
    new Person(2,"Ana","Figueiredo"),
    new Person(3,"Ricardo","Figueiredo"),//second Duplicate to the example
    new Person(4,"Margarida","Figueiredo"),
    new Person(5,"Ricardo","Figueiredo")//third Duplicate to the example
    };

    Console.WriteLine("All:");
    PersonsLst.ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
    /* OUTPUT:
        All:
        1 -> Ricardo Figueiredo
        2 -> Ana Figueiredo
        3 -> Ricardo Figueiredo
        4 -> Margarida Figueiredo
        5 -> Ricardo Figueiredo
        */

    Console.WriteLine("All lines with repeated data");
    PersonsLst.getAllRepeated(z => new { z.Name, z.Surname })
        .ToList()
        .ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
    /* OUTPUT:
        All lines with repeated data
        1 -> Ricardo Figueiredo
        3 -> Ricardo Figueiredo
        5 -> Ricardo Figueiredo
        */
    Console.WriteLine("Only Repeated more than once");
    PersonsLst.getMoreThanOnceRepeated(z => new { z.Name, z.Surname })
        .ToList()
        .ForEach(z => Console.WriteLine("{0} -> {1} {2}", z.Id, z.Name, z.Surname));
    /* OUTPUT:
        Only Repeated more than once
        3 -> Ricardo Figueiredo
        5 -> Ricardo Figueiredo
        */
}

1
Erwägen Sie die Verwendung von Skip (1) .Any () anstelle von Count (). Wenn Sie 1000 Duplikate haben, wird Skip (1) .Any () beendet, nachdem das zweite gefunden wurde. Count () greift auf alle 1000 Elemente zu.
Harald Coppoolse

1
Wenn Sie diese Erweiterungsmethode hinzufügen, sollten Sie HashSet.Add anstelle von GroupBy verwenden, wie in einer der anderen Antworten angegeben. Sobald HashSet.Add ein Duplikat findet, wird es gestoppt. Ihr GroupBy wird weiterhin alle Elemente gruppieren, auch wenn eine Gruppe mit mehr als einem Element gefunden wurde
Harald Coppoolse

6

So finden Sie nur die doppelten Werte:

var duplicates = list.GroupBy(x => x.Key).Any(g => g.Count() > 1);

Z.B. var list = new [] {1,2,3,1,4,2};

Gruppieren nach gruppiert also die Zahlen nach ihren Schlüsseln und behält die Anzahl (Anzahl der Wiederholungen) bei. Danach überprüfen wir nur die Werte, die sich mehr als einmal wiederholt haben.

So finden Sie nur die eindeutigen Werte:

var unique = list.GroupBy(x => x.Key).All(g => g.Count() == 1);

Z.B. var list = new [] {1,2,3,1,4,2};

Gruppieren nach gruppiert also die Zahlen nach ihren Schlüsseln und behält die Anzahl (Anzahl der Wiederholungen) bei. Danach überprüfen wir nur noch, ob die Werte, die sich nur einmal wiederholt haben, eindeutig sind.


Der folgende Code enthält auch eindeutige Elemente. var unique = list.Distinct(x => x)
Malu MN

1

Vollständiger Satz von Linq to SQL-Erweiterungen von Duplikatfunktionen, die in MS SQL Server überprüft wurden. Ohne Verwendung von .ToList () oder IEnumerable. Diese Abfragen werden in SQL Server und nicht im Speicher ausgeführt. . Die Ergebnisse werden nur im Speicher zurückgegeben.

public static class Linq2SqlExtensions {

    public class CountOfT<T> {
        public T Key { get; set; }
        public int Count { get; set; }
    }

    public static IQueryable<TKey> Duplicates<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
        => source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(s => s.Key);

    public static IQueryable<TSource> GetDuplicates<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
        => source.GroupBy(groupBy).Where(w => w.Count() > 1).SelectMany(s => s);

    public static IQueryable<CountOfT<TKey>> DuplicatesCounts<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
        => source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(y => new CountOfT<TKey> { Key = y.Key, Count = y.Count() });

    public static IQueryable<Tuple<TKey, int>> DuplicatesCountsAsTuble<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> groupBy)
        => source.GroupBy(groupBy).Where(w => w.Count() > 1).Select(s => Tuple.Create(s.Key, s.Count()));
}

0

Es gibt eine Antwort, aber ich habe nicht verstanden, warum es nicht funktioniert.

var anyDuplicate = enumerable.GroupBy(x => x.Key).Any(g => g.Count() > 1);

Meine Lösung ist in dieser Situation so.

var duplicates = model.list
                    .GroupBy(s => s.SAME_ID)
                    .Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
    doSomething();
}
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.