Вопрос

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

Это было полезно?

Решение

Возможно, вам следует рассмотреть возможность использования Хэшсет.

По ссылке 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 = list.Count - 1;
while (index > 0)
{
    if (list[index] == list[index - 1])
    {
        if (index < list.Count - 1)
            (list[index], list[list.Count - 1]) = (list[list.Count - 1], list[index]);
        list.RemoveAt(list.Count - 1);
        index--;
    }
    else
        index--;
}

Примечания:

  • Сравнение выполняется сзади вперед, чтобы избежать необходимости просматривать список курортов после каждого удаления.
  • В этом примере теперь для замены используются кортежи значений C#. Замените их соответствующим кодом, если вы не можете его использовать.
  • Конечный результат больше не сортируется

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

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

Замените «Тип» на желаемый тип, например.инт.

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

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() и этот образец делает), чем удалять из него элементы.

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

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

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

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

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

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.

Используйте Linq Союз метод.

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

Код

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

using System.Linq;

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

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

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

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

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

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

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

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

Редактировать: А HashSet метод O(N) время и O(N) пространство при сортировке и последующем создании уникальности (как предлагает @Ласевк и другие) есть O(N*lgN) время и O(1) пространство, поэтому мне не так ясно (как это было на первый взгляд), что способ сортировки хуже (приношу извинения за временное отрицательное голосование...)

Вот метод расширения для удаления соседних дубликатов на месте.Сначала вызовите Sort() и передайте тот же IComparer.Это должно быть более эффективно, чем Лассе В.Версия Карлсена, которая неоднократно вызывает 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);
}

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

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

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

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

Это берет отдельные элементы (элементы без дублирующихся элементов) и снова преобразует их в список:

List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();

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

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

Другой способ в .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);
       }
  }

Ура Рави Ганесан

Вот простое решение, которое не требует сложного для чтения 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--)

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

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

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

Вы можете использовать Союз

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

Если у вас есть буксировочные классы Product и Customer и мы хотим удалить повторяющиеся элементы из их списка

public class Product
{
    public int Id { get; set; }
    public string ProductName { get; set; }

}

public class Customer
{
    public int Id { get; set; }
    public string CustomerName { get; set; }

}

Вы должны определить общий класс в форме ниже

public class ItemEqualityComparer<T> : IEqualityComparer<T> where T : class
{
    private readonly PropertyInfo _propertyInfo;

    public ItemEqualityComparer(string keyItem)
    {
        _propertyInfo = typeof(T).GetProperty(keyItem, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
    }

    public bool Equals(T x, T y)
    {
        var xValue = _propertyInfo?.GetValue(x, null);
        var yValue = _propertyInfo?.GetValue(y, null);
        return xValue != null && yValue != null && xValue.Equals(yValue);
    }

    public int GetHashCode(T obj)
    {
        var propertyValue = _propertyInfo.GetValue(obj, null);
        return propertyValue == null ? 0 : propertyValue.GetHashCode();
    }
}

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

var products = new List<Product>
            {
                new Product{ProductName = "product 1" ,Id = 1,},
                new Product{ProductName = "product 2" ,Id = 2,},
                new Product{ProductName = "product 2" ,Id = 4,},
                new Product{ProductName = "product 2" ,Id = 4,},
            };
var productList = products.Distinct(new ItemEqualityComparer<Product>(nameof(Product.Id))).ToList();

var customers = new List<Customer>
            {
                new Customer{CustomerName = "Customer 1" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
            };
var customerList = customers.Distinct(new ItemEqualityComparer<Customer>(nameof(Customer.Id))).ToList();

этот код удаляет повторяющиеся элементы, Id если вы хотите удалить повторяющиеся элементы по другому свойству, вы можете изменить nameof(YourClass.DuplicateProperty) такой же nameof(Customer.CustomerName) затем удалите повторяющиеся элементы с помощью CustomerName Свойство.

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

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

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

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

Насколько я понимаю, это самый быстрый и дешевый метод Я знаю (также при поддержке очень опытного программиста, специализирующегося на оптимизации физики в реальном времени).

// Duplicates will be noticed after a sort O(nLogn)
list.Sort();

// Store the current and last items. Current item declaration is not really needed, and probably optimized by the compiler, but in case it's not...
int lastItem = -1;
int currItem = -1;

int size = list.Count;

// Store the index pointing to the last item we want to keep in the list
int last = size - 1;

// Travel the items from last to first O(n)
for (int i = last; i >= 0; --i)
{
    currItem = list[i];

    // If this item was the same as the previous one, we don't want it
    if (currItem == lastItem)
    {
        // Overwrite last in current place. It is a swap but we don't need the last
       list[i] = list[last];

        // Reduce the last index, we don't want that one anymore
        last--;
    }

    // A new item, we store it and continue
    else
        lastItem = currItem;
}

// We now have an unsorted list with the duplicates at the end.

// Remove the last items just once
list.RemoveRange(last + 1, size - last - 1);

// Sort again O(n logn)
list.Sort();

Окончательная стоимость составляет:

nlogn + n + nlogn = n + 2nlogn = О(нлогн) что очень приятно.

Примечание относительно RemoveRange:Поскольку мы не можем установить количество в списке и избежать использования функций удаления, я точно не знаю скорости этой операции, но думаю, что это самый быстрый способ.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top