C # LINQ trova i duplicati nell'elenco


335

Utilizzando LINQ, da a List<int>, come posso recuperare un elenco che contiene voci ripetute più volte e i loro valori?

Risposte:


569

Il modo più semplice per risolvere il problema è raggruppare gli elementi in base al loro valore, quindi selezionare un rappresentante del gruppo se nel gruppo sono presenti più elementi. In LINQ, questo si traduce in:

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

Se vuoi sapere quante volte gli elementi vengono ripetuti, puoi usare:

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

Ciò restituirà un Listtipo anonimo e ogni elemento avrà le proprietà Elemente Counter, per recuperare le informazioni necessarie.

E infine, se è un dizionario che stai cercando, puoi usare

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

Ciò restituirà un dizionario, con l'elemento come chiave e il numero di volte che viene ripetuto come valore.


Ora solo una meraviglia, diciamo che int duplicati sono distribuiti in matrici n int, sto usando il dizionario e per il ciclo per capire quale array contiene un duplicato e rimuoverlo secondo una logica di distribuzione, c'è un modo più veloce (linq chiedendo) di raggiungere quel risultato? grazie in anticipo per l'interesse.
Mirko Arcese,

Sto facendo qualcosa del genere: code for (int i = 0; i <duplicates.Count; i ++) {int duplicate = duplicates [i]; duplicatesLocation.Add (duplicato, nuovo Elenco <int> ()); for (int k = 0; k <hitsList.Length; k ++) {if (hitsList [k] .Contains (duplicate)) {duplicatesLocation.ElementAt (i) .Value.Add (k); }} // rimuove i duplicati secondo alcune regole. }code
Mirko Arcese,

se vuoi trovare duplicati in un elenco di array, dai un'occhiata a SelectMany
Salva il

Sto cercando duplicati in una serie di elenchi, ma non sono riuscito a capire in che modo molti possono aiutarmi a distinguerlo
Mirko Arcese,

1
Per verificare se una raccolta ha più di un elemento se è più efficiente usare Skip (1) .Any () invece di Count (). Immagina una collezione con 1000 elementi. Skip (1). Qualsiasi () rileverà che c'è più di 1 quando trova il 2 ° elemento. L'uso di Count () richiede l'accesso alla raccolta completa.
Harald Coppoolse,

133

Scopri se un enumerabile contiene duplicati :

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

Scopri se tutti i valori in un elenco sono unici :

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

C'è qualche possibilità che questi non siano sempre opposti booleani? anyDuplicate ==! allUnique in tutti i casi.
Garr Godfrey,

1
@GarrGodfrey Sono sempre opposti booleani
Caltor,

21

Un altro modo è usare HashSet:

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

Se desideri valori univoci nell'elenco dei duplicati:

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

Ecco la stessa soluzione di un metodo di estensione generico:

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

Questo non funziona come previsto. Usando List<int> { 1, 2, 3, 4, 5, 2 }come sorgente, il risultato è un IEnumerable<int>elemento con il valore di 1(dove il valore duplicato corretto è 2)
BCA

@BCA ieri, penso che ti sbagli. Dai
HuBeZa,

Il violino stampa il risultato corretto. Tuttavia, ho aggiunto la linea Console.WriteLine("Count: {0}", duplicates.Count());direttamente sotto di essa e viene stampata 6. A meno che non mi manchi qualcosa sui requisiti per questa funzione, dovrebbe esserci solo 1 articolo nella raccolta risultante.
BCA,

@BCA ieri, è un bug causato dall'esecuzione differita di LINQ. Ho aggiunto ToListper risolvere il problema, ma significa che il metodo viene eseguito non appena viene chiamato e non quando si esegue l'iterazione sui risultati.
HuBeZa,

var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));porterà a un elenco che include tutte le occorrenze di duplicati. Quindi se hai quattro occorrenze di 2 nella tua lista, la tua lista duplicata conterrà tre occorrenze di 2, poiché solo una delle 2 può essere aggiunta a HashSet. Se si desidera che l'elenco contenga valori univoci per ciascun duplicato, utilizzare invece questo codice:var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
solid_luffy

10

Puoi farlo:

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

Con questi metodi di estensione:

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

L'uso di IsMultiple () nel metodo Duplicati è più veloce di Count () perché non esegue l'iterazione dell'intera raccolta.


Se osservi l' origine di riferimento per il raggruppamento , puoi vedere che Count() è pre-calcolato e la tua soluzione è probabilmente più lenta.
Johnbot,

@Johnbot. Hai ragione, in questo caso è più veloce e l'implementazione probabilmente non cambierà mai ... ma dipende da un dettaglio di implementazione della classe di implementazione dietro IGrouping. Con la mia implementazione, sai che non itererà mai l'intera collezione.
Alex Siepman,

quindi contare [ Count()] è sostanzialmente diverso dall'iterare l'intero elenco. Count()è pre-calcolato ma non ripetendo l'intera lista.
Jogi,

@rehan khan: non capisco la differenza tra Count () e Count ()
Alex Siepman,

2
@RehanKhan: IsMultiple NON sta eseguendo un Count (), si interrompe immediatamente dopo 2 elementi. Proprio come Take (2) .Count> = 2;
Alex Siepman,

6

Ho creato un'estensione per rispondere a questo che potresti includerlo nei tuoi progetti, penso che questo ritorni il più dei casi quando cerchi duplicati in List o Linq.

Esempio:

//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
Prendi in considerazione l'utilizzo di Skip (1) .Any () invece di Count (). Se hai 1000 duplicati, salta (1). Qualsiasi () si fermerà dopo aver trovato il secondo. Count () accederà a tutti i 1000 elementi.
Harald Coppoolse,

1
Se aggiungi questo metodo di estensione, considera l'utilizzo di HashSet.Add invece di GroupBy, come suggerito in una delle altre risposte. Non appena HashSet.Add trova un duplicato, si interromperà. Il GroupBy continuerà a raggruppare tutti gli elementi, anche se è stato trovato un gruppo con più di un elemento
Harald Coppoolse,

6

Per trovare solo i valori duplicati:

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

Per esempio. var list = new [] {1,2,3,1,4,2};

quindi raggruppa raggrupperà i numeri con i loro tasti e manterrà il conteggio (numero di volte che è stato ripetuto) con esso. Successivamente, stiamo solo verificando i valori che si sono ripetuti più di una volta.

Per trovare solo i valori uniuqe:

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

Per esempio. var list = new [] {1,2,3,1,4,2};

quindi raggruppa raggrupperà i numeri con i loro tasti e manterrà il conteggio (numero di volte che è stato ripetuto) con esso. Dopodiché, stiamo solo verificando i valori che hanno ripetuto solo una volta che i mezzi sono unici.


Sotto il codice troverai anche oggetti unici. var unique = list.Distinct(x => x)
Malu MN,

1

Set completo di estensioni Linq-SQL delle funzioni duplicate controllate in MS SQL Server. Senza usare .ToList () o IEnumerable. Queste query vengono eseguite in SQL Server anziché in memoria. . I risultati ritornano solo in memoria.

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

c'è una risposta ma non ho capito perché non funziona;

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

la mia soluzione è così in questa situazione;

var duplicates = model.list
                    .GroupBy(s => s.SAME_ID)
                    .Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
    doSomething();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.