C # LINQ Listede yinelenenleri bul


334

LINQ kullanarak a'dan List<int>birden çok kez tekrarlanan girdileri ve değerlerini içeren bir listeyi nasıl alabilirim?

Yanıtlar:


568

Sorunu çözmenin en kolay yolu, öğeleri değerlerine göre gruplandırmak ve sonra grupta birden fazla öğe varsa grubun bir temsilcisini seçmektir. LINQ'da bu şu anlama gelir:

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

Elemanların kaç kez tekrarlandığını bilmek istiyorsanız, şunları kullanabilirsiniz:

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

Bu, Listanonim türden bir a döndürür ve her öğe, ihtiyacınız olan bilgileri almak için Elementve özelliklerine sahip olacaktır Counter.

Ve son olarak, eğer aradığınız bir sözlükse,

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

Bu, öğeniz anahtar olarak ve kaç kez değer olarak tekrarlandığıyla bir sözlük döndürür.


Şimdi sadece bir merak, diyelim ki yinelenen int n int dizilere dağıtılıyor, sözlük kullanarak im ve hangi dizinin yinelenen bir dizi içerdiğini anlamak ve onu bir dağıtım mantığına göre kaldırmak için en hızlı yol var (linq merak) sonuç elde edilsin mi? ilginiz için şimdiden teşekkür ederim.
Mirko Arcese

Ben böyle bir şey yapıyorum: code for (int i = 0; i <duplicates.Count; i ++) {int duplicate = duplicates [i]; duplicatesLocation.Add (yinelenen, yeni Liste <int> ()); for (int k = 0; k <hitsList.Length; k ++) {if (hitsList [k]. içerir (kopya)) {duplicatesLocation.ElementAt (i) .Value.Add (k); }} // kopyaları bazı kurallara göre kaldırın. }code
Mirko Arcese

diziler listesinde yinelenenleri bulmak istiyorsanız, SelectMany'ye bir göz atın
Kaydet

Bir dizi listede yinelenenleri arıyorum, ancak selectmany bunu yapmak için bana nasıl yardımcı olabilir alamadım
Mirko Arcese

1
Count () yerine Atla (1) .Any () kullanmak daha verimli olup olmadığını herhangi bir koleksiyonda birden fazla öğe olup olmadığını kontrol etmek için. 1000 element içeren bir koleksiyon düşünün. Atla (1) .Any (), 2. elemanı bulduğunda 1'den fazla olduğunu algılar. Count () kullanmak, tüm koleksiyona erişmenizi gerektirir.
Harald Coppoolse

133

Bir numaralandırılabilir herhangi bir kopya içerip içermediğini öğrenin :

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

Numaralandırılabilir bir değerdeki tüm değerlerin benzersiz olup olmadığını öğrenin :

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

Bunların her zaman boole karşıtları olmaması ihtimali var mı? Tüm durumlarda anyDuplicate ==! allUnique.
Garr Godfrey

1
@GarrGodfrey Her zaman boole karşıtlarıdır
Caltor

21

Başka bir yol kullanmak HashSet:

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

Yinelenen listenizde benzersiz değerler istiyorsanız:

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

İşte genel bir uzantı yöntemi ile aynı çözüm:

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

Bu beklendiği gibi çalışmaz. List<int> { 1, 2, 3, 4, 5, 2 }Kaynak olarak kullanıldığında sonuç, (doğru yinelenen değer 2 olduğunda) değerine IEnumerable<int>sahip bir öğeye sahip olur1
BCA

@BCA dün, bence yanılıyorsun. Bu örneğe göz atın: dotnetfiddle.net/GUnhUl
HuBeZa

Kemanınız doğru sonucu yazdırır. Ancak, çizgiyi Console.WriteLine("Count: {0}", duplicates.Count());doğrudan altına ekledim ve basıyor 6. Bu işleve ilişkin gereksinimlerle ilgili bir şey eksik olmadıkça, ortaya çıkan koleksiyonda yalnızca 1 öğe olmalıdır.
BCA

@BCA dün, LINQ ertelenmiş yürütmenin neden olduğu bir hata. ToListSorunu düzeltmek için ekledim , ancak sonuçların üzerinde yinelediğinizde değil, yöntemin en kısa sürede yürütüldüğü anlamına gelir.
HuBeZa

var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));tüm kopyaları içeren bir listeye yönlendirir. Bu nedenle, listenizde dört kez 2 tekrarınız varsa, yinelenen listeniz 2'den üç kez içerecektir, çünkü 2'den yalnızca biri HashSet'e eklenebilir. Listenizin her bir kopya için benzersiz değerler içermesini istiyorsanız bunun yerine şu kodu kullanın:var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
solid_luffy

10

Bunu yapabilirsiniz:

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

Bu genişletme yöntemleriyle:

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

Duplicates yönteminde IsMultiple () yöntemini kullanmak Count () yönteminden daha hızlıdır, çünkü bu işlem tüm koleksiyonu yinelemez.


Eğer bakarsak Gruplama için referans kaynağı bunu görebilirsiniz Count() edilir bilgisayarlı ve çözüm olasılıkla yavaştır önceden.
Johnbot

@Johnbot. Haklısınız, bu durumda daha hızlı ve uygulama muhtemelen değişmeyecek ... ancak IGrouping'in arkasındaki uygulama sınıfının bir uygulama detayına bağlı. Uygulamamla birlikte, tüm koleksiyonu asla tekrarlamayacağını biliyorsunuz.
Alex Siepman

dolayısıyla [ Count()] sayımı temelde tüm listeyi yinelemekten farklıdır. Count()önceden hesaplanmıştır ancak tüm listeyi yinelemek değildir.
Jogi

@rehan khan: Count () ve Count () arasındaki farkı anlamıyorum
Alex Siepman

2
@ RehanKhan: IsMultiple bir Count () yapmıyor, 2 öğeden hemen sonra duruyor. Tıpkı Take (2) gibi. => 2;
Alex Siepman

6

Bunu projelerinize dahil edebileceğiniz bir yanıt uzantısı oluşturdum, List veya Linq'de kopyalar aradığınızda bu durumun en çok geri döndüğünü düşünüyorum.

Misal:

//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
Count () yerine Skip (1) .Any () kullanmayı düşünün. 1000 kopyaya sahipseniz, Atla (1) .Herhangi bir () 2. tanesini bulduktan sonra durur. Count (), 1000 öğenin tümüne erişir.
Harald Coppoolse

1
Bu uzantı yöntemini eklerseniz, diğer yanıtlardan birinde önerildiği gibi GroupBy yerine HashSet.Add kullanmayı düşünün. HashSet.Add bir kopya bulur bulmaz durur. GroupBy'niz, birden fazla öğeye sahip bir grup bulunsa bile tüm öğeleri gruplandırmaya devam edecektir
Harald Coppoolse

6

Yalnızca yinelenen değerleri bulmak için:

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

Örneğin. var list = yeni [] {1,2,3,1,4,2};

bu nedenle grupla, sayıları tuşlarına göre gruplayacak ve sayıyı (tekrarlama sayısını) koruyacaktır. Bundan sonra, sadece bir kereden fazla tekrar eden değerleri kontrol ediyoruz.

Yalnızca uniuqe değerlerini bulmak için:

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

Örneğin. var list = yeni [] {1,2,3,1,4,2};

bu nedenle grupla, sayıları tuşlarına göre gruplayacak ve sayıyı (tekrarlama sayısını) koruyacaktır. Bundan sonra, sadece bir kez tekrarlanan değerleri benzersiz olan kontrol ediyoruz.


Aşağıdaki kod benzersiz öğeler de bulacaksınız. var unique = list.Distinct(x => x)
Malu MN

1

MS SQL Server'da denetlenen Yinelenen işlevlerin Linq - SQL uzantıları kümesi. .ToList () veya IEnumerable kullanmadan. Bu sorgular bellek yerine SQL Server'da yürütülür. . Sonuçlar yalnızca belleğe döner.

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

bir cevap var ama neden çalışmadığını anlamadım;

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

benim çözümüm bu durumda böyledir;

var duplicates = model.list
                    .GroupBy(s => s.SAME_ID)
                    .Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
    doSomething();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.