2008-09-06 3 views

ответ

192

Возможно, вам следует рассмотреть возможность использования 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 } 
*/ 
42

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

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

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

Если я не ошибаюсь, большинство упомянутых выше подходов являются просто абстракциями этой самой процедуры, верно? Я бы взял ваш подход здесь, Лассе, потому что его, как я мысленно рисую, перемещаясь по данным. Но теперь меня интересуют различия в производительности между некоторыми предложениями. – 2009-08-11 20:52:47

+7

Реализуйте их и время их, только чтобы быть уверенным. Даже нотация Big-O не поможет вам с фактическими показателями производительности, а только с отношением к эффекту роста. – 2009-08-12 07:03:30

+1

Мне нравится этот подход, он более переносим на другие языки. – 2012-05-14 00:26:36

10

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

+5

в C# это будет: Список noDupes = новый Список (новый HashSet (список)); list.Clear(); list.AddRange (noDupes); – SKandeel 2012-04-16 14:45:57

4

Если вы не заботитесь о порядке вы можете просто засунуть предметы в 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)); 

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

125

Как насчет: -

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

In .net 3.5?

671

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

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

Этот код не будет выполнен с ошибкой .Distinct() возвращает IEnumerable . Вы должны добавить .ToList() к нему. – ljs 2008-09-06 20:21:55

+0

Этот подход может использоваться только для списка с простыми значениями. – Polaris 2010-11-08 07:06:01

+13

Нет, он работает со списками, содержащими объекты любого типа. Но вам придется переопределить сопоставитель по умолчанию для вашего типа. Например: public override bool Equals (object obj) {...} – BaBu 2010-12-09 14:27:49

21

Как говорится в kronoz .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; 
} 

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

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

+0

Проблема с этим подходом состоит в том, что это O (N^2) -ish, в отличие от хэшета. Но, по крайней мере, очевидно, что он делает. – 2009-01-29 18:25:06

+1

@DrJokepu - на самом деле я не понимал, что конструктор `HashSet` дедуплирован, что делает его лучше для большинства обстоятельств. Тем не менее, это сохранит порядок сортировки, который `HashSet` не делает. – Keith 2010-08-24 14:59:34

+1

HashSet было представлено в 3.5 – thorn 2011-11-05 19:00:35

78

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

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

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

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

Метод расширения может быть достойной способ пойти ... примерно так:

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

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

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

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

Вот метод расширения для удаления смежных дубликатов на месте. Сначала вызовите Сортировка() и перейдите в тот же 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); 
} 
1

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

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

0

Вот простое решение, которое не требуется любое трудночитаемое 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 
       } 
      } 
     } 
    } 
+15

, который легче читать, чем Linq? – 2012-10-17 20:41:19

3

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

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

Я хотел бы использовать эту команду:

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

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

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

31

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

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

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

0

David J. является хорошим способом, нет необходимости в дополнительных объектов, сортировка и т.д. Это может быть улучшено на однако:

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

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

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

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

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

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

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

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

IEnumerable<Catalouge> distinctCatalouges = catalouges.DistinctBy(c => c.CatalougeCode); 
1

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

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

Использовать Linq в Union метод.

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

Код

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

using System.Linq; 

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

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

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

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

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

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

Смежные вопросы