Удаление дубликатов из списка в C#

У кого-нибудь есть быстрый метод для удаления дублирования общего списка в C#?

23 ответов


Возможно, вам следует рассмотреть с помощью поиска HashSet.

из ссылки MSDN:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        HashSet<int> evenNumbers = new HashSet<int>();
        HashSet<int> oddNumbers = new HashSet<int>();

        for (int i = 0; i < 5; i++)
        {
            // Populate numbers with just even numbers.
            evenNumbers.Add(i * 2);

            // Populate oddNumbers with just odd numbers.
            oddNumbers.Add((i * 2) + 1);
        }

        Console.Write("evenNumbers contains {0} elements: ", evenNumbers.Count);
        DisplaySet(evenNumbers);

        Console.Write("oddNumbers contains {0} elements: ", oddNumbers.Count);
        DisplaySet(oddNumbers);

        // Create a new HashSet populated with even numbers.
        HashSet<int> numbers = new HashSet<int>(evenNumbers);
        Console.WriteLine("numbers UnionWith oddNumbers...");
        numbers.UnionWith(oddNumbers);

        Console.Write("numbers contains {0} elements: ", numbers.Count);
        DisplaySet(numbers);
    }

    private static void DisplaySet(HashSet<int> set)
    {
        Console.Write("{");
        foreach (int i in set)
        {
            Console.Write(" {0}", i);
        }
        Console.WriteLine(" }");
    }
}

/* This example produces output similar to the following:
 * evenNumbers contains 5 elements: { 0 2 4 6 8 }
 * oddNumbers contains 5 elements: { 1 3 5 7 9 }
 * numbers UnionWith oddNumbers...
 * numbers contains 10 elements: { 0 2 4 6 8 1 3 5 7 9 }
 */

Если вы используете .Net 3+, Вы можете использовать Linq.

List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();

Как насчет:-

var noDupes = list.Distinct().ToList();

в .net 3.5 с?


просто инициализируйте HashSet со списком того же типа:

var noDupes = new HashSet<T>(withDupes);

или, если вы хотите вернуть список:

var noDupsList = new HashSet<T>(withDupes).ToList();

сортируйте его, затем проверьте два и два рядом друг с другом, так как дубликаты будут сгущаться вместе.

что-то вроде этого:

list.Sort();
Int32 index = 0;
while (index < list.Count - 1)
{
    if (list[index] == list[index + 1])
        list.RemoveAt(index);
    else
        index++;
}

это сработало для меня. просто используйте

List<Type> liIDs = liIDs.Distinct().ToList<Type>();

замените "Type" на нужный тип, например int.


Мне нравится использовать эту команду:

List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
                                                 .GroupBy(s => s.City)
                                                 .Select(grp => grp.FirstOrDefault())
                                                 .OrderBy(s => s.City)
                                                 .ToList();

У меня есть эти поля в моем списке: Id, StoreName, City, PostalCode Я хотел показать список городов в раскрывающемся списке, который имеет повторяющиеся значения. решение: группа по городу затем выберите первый для списка.

надеюсь, это поможет:)


как сказал кроноз в .Net 3.5 вы можете использовать Distinct().

в .Net 2 Вы можете имитировать его:

public IEnumerable<T> DedupCollection<T> (IEnumerable<T> input) 
{
    var passedValues = new HashSet<T>();

    // Relatively simple dupe check alg used as example
    foreach(T item in input)
        if(passedValues.Add(item)) // True if item is new
            yield return item;
}

Это может быть использовано для дедупликации любой коллекции и будет возвращать значения в исходном порядке.

обычно гораздо быстрее фильтровать коллекцию (как и Distinct() и этот образец делает), чем было бы удалить элементы из него.


метод расширения может быть достойный путь... что-то вроде этого:--3-->

public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
    return listToDeduplicate.Distinct().ToList();
}

а затем вызовите вот так, например:

List<int> myFilteredList = unfilteredList.Deduplicate();

в Java (я предполагаю, что C# более или менее идентичен):

list = new ArrayList<T>(new HashSet<T>(list))

Если вы действительно хотели, чтобы изменить исходный список:

List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);

чтобы сохранить порядок, просто замените HashSet на LinkedHashSet.


если вы не заботитесь о заказе вы можете просто засунуть вещи в HashSet, Если do хотите поддержать заказ вы можете сделать что-то вроде этого:

var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
    if (hs.Add(t))
        unique.Add(t);

или путь Linq:

var hs = new HashSet<T>();
list.All( x =>  hs.Add(x) );

Edit: на HashSet метод O(N) время и O(N) пространство при сортировке, а затем делает уникальным (как предложено @lassevk и другие)O(N*lgN) время и O(1) пространство, так что это не так ясно для меня (как это было на первый взгляд), что способ сортировки уступает (мои извинения за временное голосование вниз...)


как вспомогательный метод (без Linq):

public static List<T> Distinct<T>(this List<T> list)
{
    return (new HashSet<T>(list)).ToList();
}

использовать LINQ это Союз метод.

Примечание: это решение не требует знания Linq, кроме того, что оно существует.

код

начните с добавления следующего в верхнюю часть файла класса:

using System.Linq;

теперь вы можете использовать следующее Для удаления дубликатов из объекта с именем,obj1:

obj1 = obj1.Union(obj1).ToList();

Примечание: Переименование obj1 на имя объект.

как это работает

  1. команда Union перечисляет по одному из каждой записи двух исходных объектов. Поскольку obj1 является обоими исходными объектами, это уменьшает obj1 до одного из каждой записи.

  2. на ToList() возвращает новый список. Это необходимо, потому что команды Linq любят Union возвращает результат как результат IEnumerable вместо изменения исходного списка или возврата нового Список.


вот метод расширения для удаления соседних дубликатов на месте. Сначала вызовите Sort () и передайте тот же IComparer. Это должно быть более эффективно, чем версия Lasse V.Karlsen, которая вызывает RemoveAt повторно (что приводит к нескольким перемещениям блочной памяти).

public static void RemoveAdjacentDuplicates<T>(this List<T> List, IComparer<T> Comparer)
{
    int NumUnique = 0;
    for (int i = 0; i < List.Count; i++)
        if ((i == 0) || (Comparer.Compare(List[NumUnique - 1], List[i]) != 0))
            List[NumUnique++] = List[i];
    List.RemoveRange(NumUnique, List.Count - NumUnique);
}

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

if(items.IndexOf(new_item) < 0) 
    items.add(new_item)

вы можете использовать Union

obj2 = obj1.Union(obj1).ToList();

другой способ в .Net 2.0

    static void Main(string[] args)
    {
        List<string> alpha = new List<string>();

        for(char a = 'a'; a <= 'd'; a++)
        {
            alpha.Add(a.ToString());
            alpha.Add(a.ToString());
        }

        Console.WriteLine("Data :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t); });

        alpha.ForEach(delegate (string v)
                          {
                              if (alpha.FindAll(delegate(string t) { return t == v; }).Count > 1)
                                  alpha.Remove(v);
                          });

        Console.WriteLine("Unique Result :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t);});
        Console.ReadKey();
    }

есть много способов, чтобы решить - проблема дубликатов в списке, ниже один из них:

List<Container> containerList = LoadContainer();//Assume it has duplicates
List<Container> filteredList = new  List<Container>();
foreach (var container in containerList)
{ 
  Container duplicateContainer = containerList.Find(delegate(Container checkContainer)
  { return (checkContainer.UniqueId == container.UniqueId); });
   //Assume 'UniqueId' is the property of the Container class on which u r making a search

    if(!containerList.Contains(duplicateContainer) //Add object when not found in the new class object
      {
        filteredList.Add(container);
       }
  }

Ура Рави Ганесан!--2-->


вот простое решение, которое не требует жесткого чтения LINQ или какой-либо предварительной сортировки списка.

   private static void CheckForDuplicateItems(List<string> items)
    {
        if (items == null ||
            items.Count == 0)
            return;

        for (int outerIndex = 0; outerIndex < items.Count; outerIndex++)
        {
            for (int innerIndex = 0; innerIndex < items.Count; innerIndex++)
            {
                if (innerIndex == outerIndex) continue;
                if (items[outerIndex].Equals(items[innerIndex]))
                {
                    // Duplicate Found
                }
            }
        }
    }

ответ Дэвида Дж. - хороший метод, нет необходимости в дополнительных объектах, сортировке и т. д. Его можно улучшить, однако:

for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)

таким образом, внешний цикл идет сверху вниз для всего списка, но внутренний цикл идет снизу "до тех пор, пока не будет достигнуто положение внешнего цикла".

внешний цикл гарантирует, что весь список обработан, внутренний цикл находит фактические дубликаты, которые могут произойти только в той части, которую внешний цикл не обработал еще.

или если вы не хотите делать снизу вверх для внутреннего цикла, вы можете начать внутренний цикл с outerIndex + 1.


  public static void RemoveDuplicates<T>(IList<T> list )
  {
     if (list == null)
     {
        return;
     }
     int i = 1;
     while(i<list.Count)
     {
        int j = 0;
        bool remove = false;
        while (j < i && !remove)
        {
           if (list[i].Equals(list[j]))
           {
              remove = true;
           }
           j++;
        }
        if (remove)
        {
           list.RemoveAt(i);
        }
        else
        {
           i++;
        }
     }  
  }

установка MoreLINQ пакет через Nuget, вы можете легко отличить список объектов по свойству

IEnumerable<Catalogue> distinctCatalogues = catalogues.DistinctBy(c => c.CatalogueCode); 

простая интуитивная реализация:

public static List<PointF> RemoveDuplicates(List<PointF> listPoints)
{
    List<PointF> result = new List<PointF>();

    for (int i = 0; i < listPoints.Count; i++)
    {
        if (!result.Contains(listPoints[i]))
            result.Add(listPoints[i]);
        }

        return result;
    }