Знайдіть у списку дублікати C # LINQ


333

Використовуючи LINQ з List<int>, як я можу отримати список, який містить записи, повторювані більше одного разу, та їх значення?

Відповіді:


567

Найпростіший спосіб вирішити проблему - згрупувати елементи на основі їх значення, а потім вибрати представника групи, якщо в групі більше одного елемента. У LINQ це означає:

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

Якщо ви хочете знати, скільки разів елементи повторюються, ви можете використовувати:

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

Це поверне Listанонімний тип, і кожен елемент матиме властивості Elementта Counter, щоб отримати потрібну інформацію.

І нарешті, якщо це словник, який ви шукаєте, ви можете використовувати

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

Це поверне словник із вашим елементом як ключовим та кількістю повторень як значення.


Тепер просто дивно, скажімо, що дублюється int поширюється на n масиви int, я за допомогою словника і для циклу зрозуміти, який масив містить дублікат, і видаліть його відповідно до логіки розподілу, чи є найшвидший спосіб досягти цього результату? заздалегідь дякую за інтерес.
Мірко Арсез

Я роблю щось подібне: code for (int i = 0; i <duplicates.Count; i ++) {int duplicate = duplicates [i]; duplicatesLocation.Add (дублікат, новий Список <int> ()); for (int k = 0; k <hitsList.Length; k ++) {if (hitsList [k] .Contains (duplicate)) {duplicatesLocation.ElementAt (i) .Value.Add (k); }} // видаліть дублікати відповідно до деяких правил. }code
Мірко Арсез

якщо ви хочете знайти дублікати у списку масивів, погляньте на SelectMany
Збережіть

Я шукаю дублікати в масиві списків, але не зрозумів, як selectmany може допомогти мені розібратися
Mirko Arcese

1
Щоб перевірити, чи є в будь-якій колекції більше одного елемента, якщо ефективніше використовувати Skip (1) .Any () замість Count (). Уявіть собі колекцію з 1000 елементами. Пропустіть (1). Кожен () виявить, що є більше 1, коли знайде 2-й елемент. Використання Count () вимагає доступу до всієї колекції.
Харальд Коппулз

133

З'ясуйте, чи містить перелік число дублікатів :

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

З'ясуйте, чи всі значення у перелічуваному номері унікальні :

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

Чи є можливість, що це не завжди булеві протилежності? anyDuplicate ==! allУнікальний у всіх випадках.
Гарр Годфрі

1
@GarrGodfrey Вони завжди
булі

21

Іншим способом є використання HashSet:

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

Якщо ви хочете унікальних значень у вашому списку дублікатів:

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

Ось таке саме рішення, як і загальний метод розширення:

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

Це не працює, як очікувалося. Використовуючи List<int> { 1, 2, 3, 4, 5, 2 }в якості джерела, результат - це IEnumerable<int>один елемент із значенням 1(де правильне значення дубліката - 2)
BCA

@BCA вчора, я думаю, ти помилився. Ознайомтеся з цим прикладом: dotnetfiddle.net/GUnhUl
HuBeZa

Ваша скрипка виводить правильний результат. Однак я додав рядок Console.WriteLine("Count: {0}", duplicates.Count());прямо під ним і він друкується 6. Якщо я не пропускаю щось про вимоги до цієї функції, у отриманій колекції має бути лише 1 предмет.
BCA

@BCA вчора, це помилка, викликана відкладеним виконанням LINQ. Я додав ToList, щоб виправити проблему, але це означає, що метод виконується, як тільки він викликав, а не тоді, коли ви повторюєте результати.
HuBeZa

var hash = new HashSet<int>(); var duplicates = list.Where(i => !hash.Add(i));призведе до списку, який включає всі зустрічі дублікатів. Отже, якщо у вашому списку є чотири входження 2, то ваш дублікат буде містити три випадки з 2, оскільки до HashSet можна додати лише одне з двох. Якщо ви хочете, щоб ваш список містив унікальні значення для кожного дубліката, використовуйте замість цього код:var duplicates = mylist.Where(item => !myhash.Add(item)).ToList().Distinct().ToList();
solid_luffy

10

Ви можете зробити це:

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

За допомогою цих методів розширення:

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

Використання IsMultiple () у методі дублікатів швидше, ніж Count (), оскільки це не повторює всю колекцію.


Якщо ви подивіться на довідковий джерело для угруповання Ви можете бачити , що Count() це заздалегідь обчислений і ваше рішення, ймовірно , повільніше.
Джонбот

@Johnbot. Ви маєте рацію, в цьому випадку це швидше, і реалізація, ймовірно, ніколи не зміниться ... але це залежить від деталей реалізації класу реалізації за IGrouping. З моєї реалізації, ви знаєте, що це ніколи не повторить всю колекцію.
Алекс Сіпман

тому підрахунок [ Count()] в принципі інший, ніж повторення всього списку. Count()попередньо обчислюється, але ітерація всього списку не є.
Jogi

@rehan khan: Я не розумію різниці між Count () та Count ()
Alex Siepman

2
@RehanKhan: IsMultiple НЕ робить Count (), він зупиняється відразу після 2-х елементів. Так само, як Take (2) .Count> = 2;
Алекс Сіпман

6

Я створив бажання відповісти на це, ви могли б включити його у свої проекти, я вважаю, що це найбільше випадків, коли ви шукаєте дублікати в List або Linq.

Приклад:

//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
Подумайте про використання Skip (1) .Any () замість Count (). Якщо у вас 1000 дублікатів, тоді Skip (1). Кожен () зупиниться після того, як він знайде 2-й. Count () отримає доступ до всіх 1000 елементів.
Харальд Коппулз

1
Якщо ви додасте цей метод розширення, подумайте про використання HashSet.Add замість GroupBy, як це запропоновано в одній з інших відповідей. Як тільки HashSet.Add знайде дублікат, він зупиниться. Ваш GroupBy продовжить групувати всі елементи, навіть якщо знайдена група з більш ніж одним елементом
Харальд Коппулз

6

Щоб знайти лише дублюючі значення:

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

Напр. var list = new [] {1,2,3,1,4,2};

тому групувати по групуватиме номери за своїми клавішами та підтримуватиме кількість (кількість разів, яку вона повторювала). Після цього ми просто перевіряємо значення, які повторювались не раз.

Щоб знайти лише значення uniuqe:

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

Напр. var list = new [] {1,2,3,1,4,2};

тому групувати по групуватиме номери за своїми клавішами та підтримуватиме кількість (кількість разів, яку вона повторювала). Після цього ми просто перевіряємо значення, які повторилися лише один раз, засоби є унікальними.


Нижче в коді також знайдуться унікальні елементи. var unique = list.Distinct(x => x)
Малу МН

1

Повний набір розширень функцій дублікатів від Linq до SQL, перевірених у MS SQL Server. Без використання .ToList () або IEnumerable. Ці запити виконуються в SQL Server, а не в пам'яті. . Результати повертаються лише в пам'яті.

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

є відповідь, але я не зрозумів, чому це не працює;

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

моє рішення таке в цій ситуації;

var duplicates = model.list
                    .GroupBy(s => s.SAME_ID)
                    .Where(g => g.Count() > 1).Count() > 0;
if(duplicates) {
    doSomething();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.