C # LINQ найти дубликаты в списке


336

Используя LINQ, List<int>как я могу получить список, содержащий записи, повторенные более одного раза, и их значения?

Ответы:


569

Самый простой способ решить проблему - сгруппировать элементы по их значению, а затем выбрать представителя группы, если в группе более одного элемента. В 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 массивам, я использую словарь и цикл for, чтобы понять, какой массив содержит дубликат, и удалить его в соответствии с логикой распределения, есть ли самый быстрый способ (linq wondering) для достичь этого результата? заранее спасибо за интерес.
Мирко Arcese

Я делаю что-то вроде этого: code for (int i = 0; i <duplicates.Count; i ++) {int duplicate = duplicates [i]; duplicatesLocation.Add (duplicate, new List <int> ()); for (int k = 0; k <hitList.Length; k ++) {if (hitList [k] .Contains (duplicate)) {duplicatesLocation.ElementAt (i) .Value.Add (k); }} // удаляем дубликаты по некоторым правилам. }code
Мирко Arcese

если вы хотите найти дубликаты в списке массивов, взгляните на SelectMany
Сохранить

Я ищу дубликаты в массиве списков, но не понимаю, как selectmany может помочь мне разобраться
Мирко Arcese

1
Чтобы проверить, есть ли в какой-либо коллекции более одного элемента, эффективнее ли использовать Skip (1) .Any () вместо Count (). Представьте себе коллекцию из 1000 элементов. Пропустить (1). Любой () обнаружит более 1, когда найдет 2-й элемент. Использование Count () требует доступа ко всей коллекции.
Харальд Коппулс

134

Выясните, есть ли в перечислимом дубликат :

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 ==! allUnique во всех случаях.
Гарр Годфри

1
@GarrGodfrey Они всегда булевы противоположности
Caltor

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, так как только один из 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 () в методе Duplicates быстрее, чем Count (), потому что это не выполняет итерацию всей коллекции.


Если вы посмотрите на справочный источник для группировки, вы увидите, что Count() он предварительно рассчитан, и ваше решение, вероятно, медленнее.
Johnbot

@Johnbot. Вы правы, в этом случае это быстрее, и реализация, вероятно, никогда не изменится ... но это зависит от деталей реализации класса реализации, стоящего за IGrouping. С моей реализацией вы знаете, что она никогда не будет повторять всю коллекцию.
Алекс Сипман

поэтому подсчет [ Count()] в основном отличается от итерации всего списка. Count()предварительно вычисляется, но перебирает весь список нет.
Йоги

@rehan khan: Я не понимаю разницы между Count () и Count ()
Алекс Сипман

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) .Any () остановится после того, как найдет 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};

поэтому group by сгруппирует числа по их ключам и будет вести подсчет (количество повторений) вместе с ним. После этого мы просто проверяем значения, которые повторялись более одного раза.

Чтобы найти только уникальные значения:

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

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

поэтому group by сгруппирует числа по их ключам и будет вести подсчет (количество повторений) вместе с ним. После этого мы просто проверяем значения, которые повторяются только один раз, значит уникальны.


Ниже код также найдет уникальные предметы. var unique = list.Distinct(x => x)
Малу Миннесота

1

Полный набор расширений Linq to SQL для функций Duplicates, проверенных в 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.