C # LINQ znaleźć duplikaty na liście

333

Używając LINQ, od a List<int>, jak mogę pobrać listę zawierającą wpisy powtórzone więcej niż jeden raz i ich wartości?

Mirko Arcese
źródło

Odpowiedzi:

567

Najprostszym sposobem rozwiązania problemu jest zgrupowanie elementów na podstawie ich wartości, a następnie wybranie przedstawiciela grupy, jeśli w grupie jest więcej niż jeden element. W LINQ oznacza to:

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

Jeśli chcesz wiedzieć, ile razy elementy się powtarzają, możesz użyć:

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

Zwróci Listto anonimowy typ, a każdy element będzie miał właściwości Elementi Counter, aby pobrać potrzebne informacje.

I na koniec, jeśli szukasz słownika, możesz go użyć

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

Zwróci to słownik z twoim elementem jako kluczem i liczbą powtórzeń jako wartości.

Zapisać
źródło
Teraz to dziwne, powiedzmy, że zduplikowane int są dystrybuowane do n tablic int, używam słownika i dla pętli, aby zrozumieć, która tablica zawiera duplikat i usunąć go zgodnie z logiką dystrybucji, istnieje najszybszy sposób (zastanawianie się nad linq) osiągnąć ten wynik? z góry dziękuję za zainteresowanie.
Mirko Arcese
Robię coś takiego: code for (int i = 0; i <duplicates.Count; i ++) {int duplicate = duplicates [i]; duplicatesLocation.Add (duplikat, nowa lista <int> ()); for (int k = 0; k <hitsList.Length; k ++) {if (hitsList [k] .Contains (duplicate)) {duplicatesLocation.ElementAt (i) .Value.Add (k); }} // usuń duplikaty zgodnie z niektórymi regułami. }code
Mirko Arcese
jeśli chcesz znaleźć duplikaty na liście tablic, spójrz na SelectMany
Oszczędź
Szukam duplikatów w szeregu list, ale nie wiedziałem, w jaki sposób selectmany może pomóc mi to zrobić
Mirko Arcese
1
Aby sprawdzić, czy jakaś kolekcja ma więcej niż jeden element, czy bardziej efektywne jest użycie Skip (1) .Any () zamiast Count (). Wyobraź sobie kolekcję z 1000 elementami. Pomiń (1) .Każdy () wykryje, że jest więcej niż 1, gdy znajdzie drugi element. Użycie funkcji Count () wymaga dostępu do pełnej kolekcji.
Harald Coppoolse
133

Sprawdź, czy wyliczalny zawiera duplikat :

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

Sprawdź, czy wszystkie wartości w wyliczalnych są unikalne :

var allUnique = enumerable.GroupBy(x => x.Key).All(g => g.Count() == 1);
maxbeaudoin
źródło
Czy jest jakaś możliwość, że nie zawsze są to logiczne przeciwieństwa? anyDuplicate ==! allUnique we wszystkich przypadkach.
Garr Godfrey,
1
@GarrGodfrey Są to zawsze przeciwieństwa boolowskie
Caltor,
21

Innym sposobem jest użycie HashSet:

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

Jeśli chcesz mieć unikalne wartości na liście duplikatów:

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

Oto to samo rozwiązanie, co ogólna metoda rozszerzenia:

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
źródło
To nie działa zgodnie z oczekiwaniami. Wykorzystując List<int> { 1, 2, 3, 4, 5, 2 }jako źródło, wynikiem jest IEnumerable<int>jeden element o wartości 1(gdzie poprawna zduplikowana wartość to 2)
BCA
@BCA wczoraj, myślę, że się mylisz. Sprawdź ten przykład: dotnetfiddle.net/GUnhUl
HuBeZa
Twoje skrzypce drukuje prawidłowy wynik. Jednak dodałem linię Console.WriteLine("Count: {0}", duplicates.Count());bezpośrednio pod nią i drukuje 6. O ile nie brakuje mi czegoś na temat wymagań dla tej funkcji, w wynikowej kolekcji powinna znajdować się tylko 1 pozycja.
BCA
@BCA wczoraj jest to błąd spowodowany odroczonym wykonaniem LINQ. Dodałem ToListw celu rozwiązania problemu, ale oznacza to, że metoda jest wykonywana natychmiast po jej wywołaniu, a nie podczas iteracji wyników.
HuBeZa
var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));doprowadzi do listy obejmującej wszystkie wystąpienia duplikatów. Jeśli więc masz cztery wystąpienia 2 na liście, twoja zduplikowana lista będzie zawierać trzy wystąpienia 2, ponieważ tylko jeden z 2 może zostać dodany do zestawu HashSet. Jeśli chcesz, aby twoja lista zawierała unikalne wartości dla każdego duplikatu, użyj tego kodu zamiast:var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
solid_luffy
10

Możesz to zrobić:

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

Dzięki tym metodom rozszerzenia:

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

Użycie IsMultiple () w metodzie Duplicates jest szybsze niż Count (), ponieważ nie iteruje całej kolekcji.

Alex Siepman
źródło
Jeśli spojrzysz na źródło odniesienia dla grupowania , zobaczysz, że Count() jest ono wstępnie obliczone, a twoje rozwiązanie prawdopodobnie jest wolniejsze.
Johnbot,
@Johnbot. Masz rację, w tym przypadku jest to szybsze i implementacja prawdopodobnie nigdy się nie zmieni ... ale zależy to od szczegółów implementacyjnych klasy implementacji stojącej za IGrouping. Dzięki mojemu wdrożeniu wiesz, że nigdy nie powtórzy całej kolekcji.
Alex Siepman,
więc liczenie [ Count()] zasadniczo różni się od iteracji całej listy. Count()jest wstępnie obliczony, ale iteracja całej listy nie.
Jogi,
@rehan khan: Nie rozumiem różnicy między Count () a Count ()
Alex Siepman
2
@RehanKhan: IsMultiple NIE wykonuje Count (), zatrzymuje się natychmiast po 2 elementach. Podobnie jak Take (2) .Count> = 2;
Alex Siepman
6

Stworzyłem rozszerzenie odpowiedzi na to, które możesz uwzględnić w swoich projektach, myślę, że to zwraca najwięcej w przypadku wyszukiwania duplikatów w List lub Linq.

Przykład:

//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
źródło
1
Rozważ użycie Skip (1) .Any () zamiast Count (). Jeśli masz 1000 duplikatów, to Skip (1). Każdy () zatrzyma się po znalezieniu drugiego. Count () uzyska dostęp do wszystkich 1000 elementów.
Harald Coppoolse
1
Jeśli dodasz tę metodę rozszerzenia, rozważ użycie HashSet.Add zamiast GroupBy, jak zapisano w jednej z pozostałych odpowiedzi. Gdy tylko HashSet.Add znajdzie duplikat, zostanie zatrzymany. Twój GroupBy będzie nadal grupował wszystkie elementy, nawet jeśli zostanie znaleziona grupa z więcej niż jednym elementem
Harald Coppoolse,
6

Aby znaleźć tylko zduplikowane wartości:

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

Na przykład. var list = new [] {1,2,3,1,4,2};

więc pogrupuj według, pogrupuje liczby według ich kluczy i zachowa z nim liczbę (liczbę powtórzeń). Następnie sprawdzamy tylko wartości, które powtórzyły się więcej niż jeden raz.

Aby znaleźć tylko wartości uniuqe:

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

Na przykład. var list = new [] {1,2,3,1,4,2};

więc pogrupuj według, pogrupuje liczby według ich kluczy i zachowa z nim liczbę (liczbę powtórzeń). Następnie sprawdzamy tylko wartości, które powtórzyły się tylko raz, gdy środki są unikalne.

LAV VISHWAKARMA
źródło
Poniższy kod zawiera również unikalne przedmioty. var unique = list.Distinct(x => x)
Malu MN
1

Pełny zestaw rozszerzeń Linq na SQL funkcji duplikatów sprawdzonych w MS SQL Server. Bez użycia .ToList () lub IEnumerable. Te zapytania są wykonywane w programie SQL Server, a nie w pamięci. . Wyniki wracają tylko do pamięci.

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
źródło
0

jest odpowiedź, ale nie rozumiem, dlaczego nie działa;

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

moje rozwiązanie jest takie w tej sytuacji;

var duplicates = model.list
                    .GroupBy(s => s.SAME_ID)
                    .Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
    doSomething();
}
Aykut Gündoğdu
źródło