wake-up-neo.net

Wie bekomme ich mit LINQ doppelte Elemente aus einer Liste?

Ich habe einen List<string> wie:

List<String> list = new List<String>{"6","1","2","4","6","5","1"};

Ich muss die doppelten Elemente in der Liste in eine neue Liste aufnehmen. Jetzt verwende ich dazu eine verschachtelte for-Schleife.

Die resultierende list enthält {"6","1"}.

Gibt es eine Idee, dies mit Hilfe von LINQ oder Lambda-Ausdrücken zu tun?

161
var duplicates = lst.GroupBy(s => s)
    .SelectMany(grp => grp.Skip(1));

Beachten Sie, dass dies alle Duplikate zurückgibt. Wenn Sie also nur wissen möchten, welche Elemente in der Quellliste doppelt vorhanden sind, können Sie Distinct auf die resultierende Sequenz anwenden oder die von Mark Byers angegebene Lösung verwenden.

230
Lee

Hier ist eine Möglichkeit, dies zu tun:

List<String> duplicates = lst.GroupBy(x => x)
                             .Where(g => g.Count() > 1)
                             .Select(g => g.Key)
                             .ToList();

Die GroupBy gruppiert die Elemente, die gleich sind, und die Where filtert die Elemente, die nur einmal vorkommen, so dass nur die Duplikate übrig bleiben.

167
Mark Byers

Hier ist eine weitere Option:

var list = new List<string> { "6", "1", "2", "4", "6", "5", "1" };

var set = new HashSet<string>();
var duplicates = list.Where(x => !set.Add(x));
37
LukeH

Ich weiß, es ist nicht die Antwort auf die ursprüngliche Frage, aber Sie können sich hier mit diesem Problem befinden.

Wenn Sie möchten, dass alle doppelten Elemente in Ihren Ergebnissen angezeigt werden, funktioniert das Folgende.

var duplicates = list
    .GroupBy( x => x )               // group matching items
    .Where( g => g.Skip(1).Any() )   // where the group contains more than one item
    .SelectMany( g => g );           // re-expand the groups with more than one item

In meiner Situation benötige ich alle Duplikate, damit ich sie in der Benutzeroberfläche als Fehler kennzeichnen kann.

22
Scott Langham

Ich habe diese Erweiterungsmethode basierend auf der Antwort von @ Lee auf das OP geschrieben. Hinweis , es wurde ein Standardparameter verwendet (der C # 4.0 erfordert). Ein überladener Methodenaufruf in C # 3.0 würde jedoch ausreichen. 

/// <summary>
/// Method that returns all the duplicates (distinct) in the collection.
/// </summary>
/// <typeparam name="T">The type of the collection.</typeparam>
/// <param name="source">The source collection to detect for duplicates</param>
/// <param name="distinct">Specify <b>true</b> to only return distinct elements.</param>
/// <returns>A distinct list of duplicates found in the source collection.</returns>
/// <remarks>This is an extension method to IEnumerable&lt;T&gt;</remarks>
public static IEnumerable<T> Duplicates<T>
         (this IEnumerable<T> source, bool distinct = true)
{
     if (source == null)
     {
        throw new ArgumentNullException("source");
     }

     // select the elements that are repeated
     IEnumerable<T> result = source.GroupBy(a => a).SelectMany(a => a.Skip(1));

     // distinct?
     if (distinct == true)
     {
        // deferred execution helps us here
        result = result.Distinct();
     }

     return result;
}
18
Michael

Hoffe das wird helfen

int[] listOfItems = new[] { 4, 2, 3, 1, 6, 4, 3 };

var duplicates = listOfItems 
    .GroupBy(i => i)
    .Where(g => g.Count() > 1)
    .Select(g => g.Key);

foreach (var d in duplicates)
    Console.WriteLine(d);
10
Thakur
  List<String> list = new List<String> { "6", "1", "2", "4", "6", "5", "1" };

    var q = from s in list
            group s by s into g
            where g.Count() > 1
            select g.First();

    foreach (var item in q)
    {
        Console.WriteLine(item);

    }
10
explorer

Ich habe versucht, dasselbe mit einer Liste von Objekten zu lösen und hatte Probleme, weil ich versuchte, die Liste der Gruppen in die ursprüngliche Liste zu packen. Also kam ich dazu, die Gruppen zu durchlaufen, um die ursprüngliche Liste mit Elementen zu packen, die Duplikate enthalten.

public List<MediaFileInfo> GetDuplicatePictures()
{
    List<MediaFileInfo> dupes = new List<MediaFileInfo>();
    var grpDupes = from f in _fileRepo
                   group f by f.Length into grps
                   where grps.Count() >1
                   select grps;
    foreach (var item in grpDupes)
    {
        foreach (var thing in item)
        {
            dupes.Add(thing);
        }
    }
    return dupes;
}
2
Jamie L.

Alle bisher genannten Lösungen führen eine GroupBy durch. Selbst wenn ich nur das erste Duplizieren benötige, werden alle Elemente der Sammlungen mindestens einmal aufgelistet.

Die folgende Erweiterungsfunktion beendet die Aufzählung, sobald ein Duplikat gefunden wurde. Es wird fortgesetzt, wenn ein nächstes Duplikat angefordert wird.

Wie immer in LINQ gibt es zwei Versionen, eine mit IEqualityComparer und eine ohne.

public static IEnumerable<TSource> ExtractDuplicates(this IEnumerable<TSource> source)
{
    return source.ExtractDuplicates(null);
}
public static IEnumerable<TSource> ExtractDuplicates(this IEnumerable<TSource source,
    IEqualityComparer<TSource> comparer);
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (comparer == null)
        comparer = EqualityCompare<TSource>.Default;

    HashSet<TSource> foundElements = new HashSet<TSource>(comparer);
    foreach (TSource sourceItem in source)
    {
        if (!foundElements.Contains(sourceItem))
        {   // we've not seen this sourceItem before. Add to the foundElements
            foundElements.Add(sourceItem);
        }
        else
        {   // we've seen this item before. It is a duplicate!
            yield return sourceItem;
        }
    }
}

Verwendungszweck:

IEnumerable<MyClass> myObjects = ...

// check if has duplicates:
bool hasDuplicates = myObjects.ExtractDuplicates().Any();

// or find the first three duplicates:
IEnumerable<MyClass> first3Duplicates = myObjects.ExtractDuplicates().Take(3)

// or find the first 5 duplicates that have a Name = "MyName"
IEnumerable<MyClass> myNameDuplicates = myObjects.ExtractDuplicates()
    .Where(duplicate => duplicate.Name == "MyName")
    .Take(5);

Bei all diesen linq-Anweisungen wird die Auflistung nur analysiert, bis die angeforderten Elemente gefunden werden. Der Rest der Sequenz wird nicht interpretiert.

IMHO ist dies ein Effizienzschub, den man berücksichtigen muss.

0