Вопрос:

Удалить дубликаты из списка <T> в C #

c# list generics duplicates

496517 просмотра

25 ответа

14164 Репутация автора

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

Автор: JC Grubbs Источник Размещён: 06.09.2008 07:15

Ответы (25)


45 плюса

292148 Репутация автора

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

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

list.Sort();
Int32 index = 0;
while (index < list.Count - 1)
{
    if (list[index] == list[index + 1])
        list.RemoveAt(index);
    else
        index++;
}
Автор: Lasse Vågsæther Karlsen Размещён: 06.09.2008 07:20

206 плюса

106844 Репутация автора

Решение

Возможно, вам следует рассмотреть возможность использования 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 }
 */
Автор: Jason Baker Размещён: 06.09.2008 07:21

10 плюса

126798 Репутация автора

В 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.

Автор: Tom Hawtin - tackline Размещён: 06.09.2008 07:29

5 плюса

74309 Репутация автора

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

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

Или Линк путь:

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

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

Автор: Motti Размещён: 06.09.2008 07:32

137 плюса

25518 Репутация автора

Как насчет:-

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

В .net 3.5?

Автор: ljs Размещён: 06.09.2008 07:56

729 плюса

19899 Репутация автора

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

List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();
Автор: Factor Mystic Размещён: 06.09.2008 07:56

22 плюса

94210 Репутация автора

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

Автор: Keith Размещён: 07.09.2008 09:44

85 плюса

21009 Репутация автора

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

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

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

var noDupsList = new HashSet<T>(withDupes).ToList();
Автор: Even Mien Размещён: 24.11.2009 08:05

12 плюса

121 Репутация автора

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

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

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

List<int> myFilteredList = unfilteredList.Deduplicate();
Автор: Geoff Taylor Размещён: 03.04.2010 01:05

1 плюс

19 Репутация автора

Еще один способ в .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();
    }
Автор: Bhasin Размещён: 10.02.2011 06:55

5 плюса

384 Репутация автора

Вот метод расширения для удаления соседних дубликатов на месте. Сначала вызовите 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);
}
Автор: gary Размещён: 25.02.2011 06:15

1 плюс

11 Репутация автора

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

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

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

Автор: Ravi Ganesan Размещён: 10.04.2011 05:02

1 плюс

21 Репутация автора

Вот простое решение, которое не требует трудно читаемого 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
                }
            }
        }
    }
Автор: David J. Размещён: 14.02.2012 12:20

3 плюса

49 Репутация автора

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

if(items.IndexOf(new_item) < 0) 
    items.add(new_item)
Автор: Chris Размещён: 29.06.2012 02:33

26 плюса

456 Репутация автора

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

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

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

Я надеюсь, что это помогает :)

Автор: Eric Размещён: 27.07.2012 06:57

30 плюса

451 Репутация автора

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

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

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

Автор: Hossein Sarshar Размещён: 15.11.2012 06:51

1 плюс

11 Репутация автора

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

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

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

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

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

Автор: Guest Размещён: 22.10.2013 11:10

1 плюс

558 Репутация автора

  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++;
        }
     }  
  }
Автор: Paul Richards Размещён: 14.05.2014 11:11

5 плюса

569 Репутация автора

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

public static List<T> Distinct<T>(this List<T> list)
{
    return (new HashSet<T>(list)).ToList();
}
Автор: Grant Размещён: 18.11.2014 09:45

3 плюса

638 Репутация автора

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

IEnumerable<Catalogue> distinctCatalogues = catalogues.DistinctBy(c => c.CatalogueCode); 
Автор: dush88c Размещён: 15.03.2017 02:51

1 плюс

27 Репутация автора

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

obj2 = obj1.Union(obj1).ToList();
Автор: flagamba Размещён: 06.08.2017 03:16

6 плюса

5276 Репутация автора

Используйте метод Linq's Union .

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

Код

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

using System.Linq;

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

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

Примечание: переименуйте obj1в название вашего объекта.

Как это устроено

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

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

Автор: Knickerless-Noggins Размещён: 13.02.2018 12:56

1 плюс

47 Репутация автора

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

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;
    }
Автор: Moctar Haiz Размещён: 19.04.2018 09:05

1 плюс

131 Репутация автора

Если у вас есть классы буксирных 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недвижимости.

Автор: Reza Jenabi Размещён: 02.12.2018 11:07

0 плюса

1 Репутация автора

Это берет различные элементы без дублирования элементов и снова преобразует их в список.

List myNoneDuplicateValue = listValueWithDuplicate.Distinct (). ToList ();

Автор: Alfred Udah Размещён: 13.03.2019 10:58
Вопросы из категории :
32x32