C # LINQ trouve des doublons dans la liste

336

À l'aide de LINQ, à partir de a List<int>, comment puis-je récupérer une liste contenant des entrées répétées plusieurs fois et leurs valeurs?

Mirko Arcese
la source

Réponses:

569

Le moyen le plus simple de résoudre le problème consiste à regrouper les éléments en fonction de leur valeur, puis à choisir un représentant du groupe s'il existe plusieurs éléments dans le groupe. Dans LINQ, cela se traduit par:

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

Si vous voulez savoir combien de fois les éléments sont répétés, vous pouvez utiliser:

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

Cela renverra un Listde type anonyme, et chaque élément aura les propriétés Elementet Counter, pour récupérer les informations dont vous avez besoin.

Et enfin, si c'est un dictionnaire que vous recherchez, vous pouvez utiliser

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

Cela retournera un dictionnaire, avec votre élément comme clé, et le nombre de fois qu'il sera répété comme valeur.

sauver
la source
Maintenant, juste une merveille, disons que les doublons int sont distribués dans n tableaux int, im en utilisant le dictionnaire et pour boucle pour comprendre quel tableau contient un doublon et le supprimer selon une logique de distribution, existe-t-il un moyen le plus rapide (linq se demandant) pour atteindre ce résultat? merci d'avance pour l'intérêt.
Mirko Arcese
Je fais quelque chose comme ceci: code for (int i = 0; i <duplicates.Count; i ++) {int duplicate = duplicates [i]; duplicatesLocation.Add (duplicate, new List <int> ()); for (int k = 0; k <hitsList.Length; k ++) {if (hitsList [k] .Contains (duplicate)) {duplicatesLocation.ElementAt (i) .Value.Add (k); }} // supprime les doublons selon certaines règles. }code
Mirko Arcese
si vous voulez trouver des doublons dans une liste de tableaux, jetez un œil à SelectMany
Save
Je recherche des doublons dans une liste de listes, mais je n'ai pas compris comment selectmany peut m'aider à le comprendre
Mirko Arcese
1
Pour vérifier si une collection a plus d'un élément, il est plus efficace d'utiliser Skip (1) .Any () au lieu de Count (). Imaginez une collection de 1000 éléments. Skip (1) .Any () détectera qu'il y a plus de 1 fois qu'il trouve le 2ème élément. L'utilisation de Count () nécessite d'accéder à la collection complète.
Harald Coppoolse
134

Découvrez si un énumérable contient des doublons :

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

Découvrez si toutes les valeurs d'un énumérable sont uniques :

var allUnique = enumerable.GroupBy(x => x.Key).All(g => g.Count() == 1);
maxbeaudoin
la source
Y a-t-il une possibilité que ce ne soient pas toujours des opposés booléens? anyDuplicate ==! allUnique dans tous les cas.
Garr Godfrey
1
@GarrGodfrey Ils sont toujours des opposés booléens
Caltor
21

Une autre façon utilise HashSet:

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

Si vous souhaitez des valeurs uniques dans votre liste de doublons:

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

Voici la même solution qu'une méthode d'extension générique:

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);
  }
}
HuBeZa
la source
Cela ne fonctionne pas comme prévu. En utilisant List<int> { 1, 2, 3, 4, 5, 2 }comme source, le résultat est un IEnumerable<int>avec un élément ayant la valeur 1(où la valeur en double correcte est 2)
BCA
@BCA hier, je pense que vous vous trompez. Découvrez cet exemple: dotnetfiddle.net/GUnhUl
HuBeZa
Votre violon imprime le résultat correct. Cependant, j'ai ajouté la ligne Console.WriteLine("Count: {0}", duplicates.Count());directement en dessous et elle s'imprime 6. À moins que je manque quelque chose sur les exigences de cette fonction, il ne devrait y avoir qu'un seul élément dans la collection résultante.
BCA
@BCA hier, c'est un bug causé par l'exécution différée de LINQ. J'ai ajouté ToListafin de résoudre le problème, mais cela signifie que la méthode est exécutée dès son appel, et non lorsque vous parcourez les résultats.
HuBeZa
var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));mènera à une liste qui inclut toutes les occurrences de doublons. Donc, si vous avez quatre occurrences de 2 dans votre liste, votre liste en double contiendra trois occurrences de 2, car une seule des 2 peut être ajoutée au HashSet. Si vous voulez que votre liste contienne des valeurs uniques pour chaque doublon, utilisez plutôt ce code:var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
solid_luffy
10

Tu peux le faire:

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

Avec ces méthodes d'extension:

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'utilisation de IsMultiple () dans la méthode Duplicates est plus rapide que Count () car cela n'itère pas toute la collection.

Alex Siepman
la source
Si vous regardez la source de référence pour le regroupement, vous pouvez voir qu'elle Count() est pré-calculée et que votre solution est probablement plus lente.
Johnbot
@Johnbot. Vous avez raison, dans ce cas, c'est plus rapide et l'implémentation est susceptible de ne jamais changer ... mais cela dépend d'un détail d'implémentation de la classe d'implémentation derrière IGrouping. Avec mon implémentation, vous savez qu'elle n'itérera jamais toute la collection.
Alex Siepman
donc compter [ Count()] est fondamentalement différent de parcourir toute la liste. Count()est pré-calculé mais l'itération de la liste entière ne l'est pas.
Jogi
@rehan khan: Je ne comprends pas la différence entre Count () et Count ()
Alex Siepman
2
@RehanKhan: IsMultiple ne fait PAS de compte (), il s'arrête immédiatement après 2 éléments. Tout comme Take (2) .Count> = 2;
Alex Siepman
6

J'ai créé une extension pour répondre à cela, vous pouvez l'inclure dans vos projets, je pense que cela revient le plus souvent lorsque vous recherchez des doublons dans List ou Linq.

Exemple:

//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
        */
}
Ricardo Figueiredo
la source
1
Pensez à utiliser Skip (1) .Any () au lieu de Count (). Si vous avez 1000 doublons, Skip (1) .Any () s'arrêtera après avoir trouvé le 2ème. Count () accède aux 1000 éléments.
Harald Coppoolse
1
Si vous ajoutez cette méthode d'extension, envisagez d'utiliser HashSet.Add à la place de GroupBy, comme suggéré dans l'une des autres réponses. Dès que HashSet.Add trouve un doublon, il s'arrête. Votre GroupBy continuera de regrouper tous les éléments, même si un groupe avec plus d'un élément a été trouvé
Harald Coppoolse
6

Pour rechercher uniquement les valeurs en double:

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

Par exemple. var list = new [] {1,2,3,1,4,2};

donc group by regroupera les nombres par leurs clés et maintiendra le compte (nombre de fois qu'il s'est répété) avec lui. Après cela, nous vérifions simplement les valeurs qui se sont répétées plus d'une fois.

Pour rechercher uniquement les valeurs uniuqe:

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

Par exemple. var list = new [] {1,2,3,1,4,2};

donc group by regroupera les nombres par leurs clés et maintiendra le compte (nombre de fois qu'il s'est répété) avec lui. Après cela, nous vérifions simplement les valeurs qui n'ont répété qu'une fois que les moyennes sont uniques.

LAV VISHWAKARMA
la source
Le code ci-dessous trouvera également des articles uniques. var unique = list.Distinct(x => x)
Malu MN
1

Ensemble complet d'extensions Linq to SQL des fonctions Duplicates vérifiées dans MS SQL Server. Sans utiliser .ToList () ou IEnumerable. Ces requêtes s'exécutent dans SQL Server plutôt qu'en mémoire. . Les résultats ne reviennent qu'en mémoire.

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()));
}
GeoB
la source
0

il y a une réponse mais je n'ai pas compris pourquoi ne fonctionne pas;

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

ma solution est comme ça dans cette situation;

var duplicates = model.list
                    .GroupBy(s => s.SAME_ID)
                    .Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
    doSomething();
}
Aykut Gündoğdu
la source