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

336

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

Мирко Arcese
источник

Ответы:

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);
maxbeaudoin
источник
Есть ли вероятность того, что это не всегда булевы противоположности? 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);
  }
}
HuBeZa
источник
Это не работает, как ожидалось. Используя 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()));
}
GeoB
источник
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();
}
Aykut Gündoğdu
источник