C # LINQ menemukan duplikat dalam Daftar

335

Menggunakan LINQ, dari a List<int>, bagaimana saya bisa mengambil daftar yang berisi entri berulang lebih dari sekali dan nilainya?

Mirko Arcese
sumber

Jawaban:

569

Cara termudah untuk menyelesaikan masalah adalah mengelompokkan elemen berdasarkan nilainya, dan kemudian memilih perwakilan grup jika ada lebih dari satu elemen dalam grup. Di LINQ, ini diterjemahkan menjadi:

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

Jika Anda ingin tahu berapa kali elemen diulang, Anda dapat menggunakan:

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

Ini akan Listmenghasilkan tipe anonim, dan setiap elemen akan memiliki properti ElementdanCounter , untuk mengambil informasi yang Anda butuhkan.

Dan terakhir, jika kamus yang Anda cari, Anda dapat menggunakannya

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

Ini akan mengembalikan kamus, dengan elemen Anda sebagai kunci, dan berapa kali itu diulang sebagai nilai.

Menyimpan
sumber
Sekarang hanya sebuah keajaiban, katakanlah int duplikat didistribusikan ke dalam array n int, saya menggunakan kamus dan untuk loop untuk memahami array mana yang berisi duplikat dan menghapusnya sesuai dengan logika distribusi, apakah ada cara tercepat (linq bertanya-tanya) untuk mencapai hasil itu? terima kasih sebelumnya atas minatnya.
Mirko Arcese
Saya melakukan sesuatu seperti ini: code for (int i = 0; i <duplicates.Count; i ++) {int duplikat = duplikat [i]; duplicatesLocation.Add (duplikat, Daftar baru <int> ()); for (int k = 0; k <hitsList.Length; k ++) {if (hitsList [k] .Berisi (duplikat)) {duplicatesLocation.ElementAt (i) .Value.Add (k); }} // menghapus duplikat sesuai dengan beberapa aturan. }code
Mirko Arcese
jika Anda ingin menemukan duplikat dalam daftar array, lihat SelectMany
Simpan
Saya mencari duplikat dalam array daftar, tetapi tidak mendapatkan bagaimana selectmany dapat membantu saya untuk keluar
Mirko Arcese
1
Untuk memeriksa apakah ada koleksi yang memiliki lebih dari satu elemen jika lebih efisien untuk menggunakan Lewati (1) .Any () alih-alih Hitung (). Bayangkan koleksi dengan 1000 elemen. Lewati (1). Setiap () akan mendeteksi ada lebih dari 1 begitu ia menemukan elemen ke-2. Menggunakan Count () harus mengakses koleksi lengkap.
Harald Coppoolse
133

Cari tahu apakah enumerable berisi duplikat :

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

Cari tahu apakah semua nilai dalam enumerable unik :

var allUnique = enumerable.GroupBy(x => x.Key).All(g => g.Count() == 1);
maxbeaudoin
sumber
Apakah ada kemungkinan ini tidak selalu berlawanan boolean? anyDuplicate ==! allUnique dalam semua kasus.
Garr Godfrey
1
@GarrGodfrey Mereka selalu berseberangan dengan boolean
Caltor
21

Cara lain menggunakan HashSet:

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

Jika Anda ingin nilai unik dalam daftar duplikat Anda:

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

Berikut adalah solusi yang sama dengan metode ekstensi generik:

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
sumber
Ini tidak berfungsi seperti yang diharapkan. Menggunakan List<int> { 1, 2, 3, 4, 5, 2 }sebagai sumber, hasilnya adalah IEnumerable<int>dengan satu elemen memiliki nilai 1(di mana nilai duplikat yang benar adalah 2)
BCA
@ BCA kemarin, saya pikir Anda salah. Lihatlah contoh ini: dotnetfiddle.net/GUnhUl
HuBeZa
Biola Anda mencetak hasil yang benar. Namun, saya menambahkan garis Console.WriteLine("Count: {0}", duplicates.Count());tepat di bawahnya dan itu mencetak 6. Kecuali saya kehilangan sesuatu tentang persyaratan untuk fungsi ini, seharusnya hanya ada 1 item dalam koleksi yang dihasilkan.
BCA
@BCA kemarin, ini adalah bug yang disebabkan oleh eksekusi LINQ yang ditangguhkan. Saya telah menambahkan ToListuntuk memperbaiki masalah ini, tetapi itu berarti bahwa metode ini dieksekusi segera setelah dipanggil, dan bukan ketika Anda mengulangi hasilnya.
HuBeZa
var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));akan mengarah ke daftar yang mencakup semua kejadian duplikat. Jadi, jika Anda memiliki empat kemunculan 2 dalam daftar Anda, maka daftar duplikat Anda akan berisi tiga kemunculan 2, karena hanya satu dari 2 yang dapat ditambahkan ke HashSet. Jika Anda ingin daftar Anda mengandung nilai-nilai unik untuk setiap duplikat, gunakan kode ini sebagai gantinya:var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
solid_luffy
10

Kamu bisa melakukan ini:

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

Dengan metode ekstensi ini:

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

Menggunakan IsMultiple () dalam metode Duplikat lebih cepat daripada Count () karena ini tidak mengulangi seluruh koleksi.

Alex Siepman
sumber
Jika Anda melihat sumber referensi bagi Pengelompokan Anda dapat melihat bahwa Count() adalah pra dihitung dan solusi Anda mungkin lebih lambat.
Johnbot
@ Johnbot. Anda benar, dalam hal ini lebih cepat dan implementasinya kemungkinan tidak akan pernah berubah ... tetapi itu tergantung pada detail implementasi kelas implemetasi di belakang IGrouping. Dengan implementasi saya, Anda tahu itu tidak akan pernah mengulangi seluruh koleksi.
Alex Siepman
jadi penghitungan [ Count()] pada dasarnya berbeda dari pengulangan seluruh daftar. Count()sudah dihitung sebelumnya tetapi iterasi seluruh daftar tidak.
Jogi
@rehan khan: Saya tidak mengerti perbedaan antara Count () dan Count ()
Alex Siepman
2
@RehanKhan: IsMultiple TIDAK melakukan hitungan (), berhenti segera setelah 2 item. Sama seperti Take (2) .Count> = 2;
Alex Siepman
6

Saya membuat ekstensi untuk menanggapi ini Anda bisa memasukkannya ke dalam proyek Anda, saya pikir ini mengembalikan kasus terbanyak ketika Anda mencari duplikat di Daftar atau Linq.

Contoh:

//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
sumber
1
Pertimbangkan menggunakan Lewati (1) .Any () alih-alih Hitung (). Jika Anda memiliki 1000 duplikat, maka Lewati (1). Setiap () akan berhenti setelah menemukan yang ke-2. Count () akan mengakses semua 1000 elemen.
Harald Coppoolse
1
Jika Anda menambahkan metode ekstensi ini, pertimbangkan untuk menggunakan HashSet.Tambahkan alih-alih GroupBy, seperti yang disarankan dalam salah satu jawaban lain. Begitu HashSet.Add menemukan duplikat itu akan berhenti. GroupBy Anda akan terus mengelompokkan semua elemen, bahkan jika grup dengan lebih dari satu elemen telah ditemukan
Harald Coppoolse
6

Untuk menemukan nilai duplikat saja:

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

Misalnya. var list = baru [] {1,2,3,1,4,2};

jadi kelompokkan dengan kelompokkan angka dengan kunci mereka dan akan mempertahankan hitungan (berapa kali itu diulang) dengan itu. Setelah itu, kami hanya memeriksa nilai-nilai yang telah diulang lebih dari satu kali.

Untuk menemukan nilai uniuqe saja:

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

Misalnya. var list = baru [] {1,2,3,1,4,2};

jadi kelompokkan dengan kelompokkan angka dengan kunci mereka dan akan mempertahankan hitungan (berapa kali itu diulang) dengan itu. Setelah itu, kami hanya memeriksa nilai-nilai yang telah diulang hanya sekali berarti unik.

LAV VISHWAKARMA
sumber
Kode di bawah ini juga akan menemukan item unik. var unique = list.Distinct(x => x)
Malu MN
1

Set lengkap Linq ke ekstensi SQL fungsi Duplikat diperiksa di MS SQL Server. Tanpa menggunakan .ToList () atau IEnumerable. Kueri ini mengeksekusi di SQL Server daripada di memori. . Hasilnya hanya kembali di memori.

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
sumber
0

ada jawaban tetapi saya tidak mengerti mengapa tidak bekerja;

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

solusi saya seperti itu dalam situasi ini;

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