Frage

Hat jemand eine schnelle Methode zum Deduplizieren einer generischen Liste in C#?

War es hilfreich?

Lösung

Vielleicht sollten Sie die Verwendung von a in Betracht ziehen HashSet.

Vom MSDN-Link:

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 }
 */

Andere Tipps

Wenn Sie .Net 3+ verwenden, können Sie Linq verwenden.

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

Wie wäre es mit:-

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

In .net 3.5?

Initialisieren Sie einfach ein HashSet mit einer Liste desselben Typs:

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

Oder wenn Sie eine Liste zurückgeben möchten:

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

Sortieren Sie es und markieren Sie dann zwei und zwei nebeneinander, da die Duplikate sonst zusammenklumpen.

Etwas wie das:

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

Anmerkungen:

  • Der Vergleich erfolgt von hinten nach vorne, um zu vermeiden, dass die Liste nach jedem Entfernen neu erstellt werden muss
  • In diesem Beispiel werden nun C#-Werttupel zum Austauschen verwendet. Wenn Sie diesen nicht verwenden können, ersetzen Sie ihn durch entsprechenden Code
  • Das Endergebnis ist nicht mehr sortiert

Bei mir hat es funktioniert.einfach nutzen

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

Ersetzen Sie „Typ“ durch den gewünschten Typ, z. B.int.

Ich verwende gerne diesen Befehl:

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

Ich habe diese Felder in meiner Liste:ID, Storename, Stadt, Postalcode Ich wollte die Liste von Städten in einem Dropdown -Wert zeigen, der doppelte Werte aufweist.Lösung:Gruppieren Sie nach Stadt und wählen Sie dann die erste Stadt für die Liste aus.

Ich hoffe, es hilft :)

Wie Kronoz in .Net 3.5 sagte, können Sie es verwenden Distinct().

In .Net 2 könnten Sie es nachahmen:

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

Dies kann zum Deduplizieren beliebiger Sammlungen verwendet werden und gibt die Werte in der ursprünglichen Reihenfolge zurück.

Normalerweise geht das Filtern einer Sammlung viel schneller (wie beides). Distinct() und dieses Beispiel tut es), als es wäre, Elemente daraus zu entfernen.

Eine Erweiterungsmethode könnte ein guter Weg sein ...etwas wie das:

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

Und rufen Sie dann zum Beispiel so auf:

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

In Java (ich gehe davon aus, dass C# mehr oder weniger identisch ist):

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

Wenn Sie die ursprüngliche Liste wirklich ändern wollten:

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

Um die Ordnung aufrechtzuerhalten, ersetzen Sie einfach HashSet durch LinkedHashSet.

Verwenden Sie Linqs Union Methode.

Notiz:Diese Lösung erfordert keine Linq-Kenntnisse, abgesehen davon, dass sie existiert.

Code

Fügen Sie zunächst Folgendes am Anfang Ihrer Klassendatei hinzu:

using System.Linq;

Jetzt können Sie Folgendes verwenden, um Duplikate aus einem Objekt mit dem Namen zu entfernen: obj1:

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

Notiz:Umbenennen obj1 zum Namen Ihres Objekts.

Wie es funktioniert

  1. Der Union-Befehl listet einen von jedem Eintrag von zwei Quellobjekten auf.Da obj1 beide Quellobjekte sind, reduziert dies obj1 auf eines von jedem Eintrag.

  2. Der ToList() gibt eine neue Liste zurück.Dies ist notwendig, da Linq-Befehle wie Union gibt das Ergebnis als IEnumerable-Ergebnis zurück, anstatt die ursprüngliche Liste zu ändern oder eine neue Liste zurückzugeben.

Wenn Ihnen die Reihenfolge egal ist, können Sie die Artikel einfach in eine schieben HashSet, wenn du Tun Wenn Sie die Reihenfolge beibehalten möchten, können Sie Folgendes tun:

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

Oder auf die Linq-Art:

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

Bearbeiten: Der HashSet Methode ist O(N) Zeit und O(N) Platz beim Sortieren und anschließenden Eindeutigmachen (wie von @ vorgeschlagen)lassevk und andere) ist O(N*lgN) Zeit und O(1) Platz, daher ist mir nicht so klar (wie es auf den ersten Blick war), dass die Sortiermethode minderwertig ist (ich entschuldige mich für die vorübergehende Ablehnung ...)

Hier ist eine Erweiterungsmethode zum Entfernen benachbarter Duplikate vor Ort.Rufen Sie zuerst Sort() auf und übergeben Sie denselben IComparer.Dies sollte effizienter sein als Lasse V.Karlsens Version, die RemoveAt wiederholt aufruft (was zu mehreren Blockspeicherverschiebungen führt).

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

Als Hilfsmethode (ohne Linq):

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

Installieren der MehrLINQ Paket über Nuget, können Sie Objektlisten einfach nach einer Eigenschaft unterscheiden

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

Dies nimmt „distinct“ (die Elemente ohne duplizierende Elemente) und wandelt sie erneut in eine Liste um:

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

Es könnte einfacher sein, einfach sicherzustellen, dass keine Duplikate zur Liste hinzugefügt werden.

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

Ein anderer Weg in .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();
    }

Es gibt viele Lösungsmöglichkeiten – das Duplikatproblem in der Liste, unten ist eine davon:

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

Prost Ravi Ganesan

Hier ist eine einfache Lösung, die weder schwer lesbares LINQ noch eine vorherige Sortierung der Liste erfordert.

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

Die Antwort von David J. ist eine gute Methode, es sind keine zusätzlichen Objekte, Sortierung usw. erforderlich.Es kann jedoch verbessert werden:

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

Die äußere Schleife bewegt sich also für die gesamte Liste von oben nach unten, die innere Schleife jedoch nach unten, „bis die Position der äußeren Schleife erreicht ist“.

Die äußere Schleife stellt sicher, dass die gesamte Liste verarbeitet wird, die innere Schleife findet die tatsächlichen Duplikate. Diese können nur in dem Teil auftreten, den die äußere Schleife noch nicht verarbeitet hat.

Oder wenn Sie die innere Schleife nicht von unten nach oben ausführen möchten, können Sie die innere Schleife bei äußerem Index + 1 beginnen lassen.

Sie können Union verwenden

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

Wenn Sie Schleppunterricht haben Product Und Customer und wir möchten doppelte Elemente aus ihrer Liste entfernen

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

}

Sie müssen im folgenden Formular eine generische Klasse definieren

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

Anschließend können Sie doppelte Elemente in Ihrer Liste entfernen.

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

Mit diesem Code werden doppelte Elemente entfernt Id Wenn Sie doppelte Elemente von anderen Eigenschaften entfernen möchten, können Sie diese ändern nameof(YourClass.DuplicateProperty) Dasselbe nameof(Customer.CustomerName) Entfernen Sie dann doppelte Elemente durch CustomerName Eigentum.

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

Eine einfache intuitive Implementierung:

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

Alle Antworten kopieren Listen oder erstellen eine neue Liste oder verwenden langsame Funktionen oder sind einfach nur schmerzhaft langsam.

Nach meinem Verständnis ist dies die schnellste und günstigste Methode Ich weiß es (auch unterstützt von einem sehr erfahrenen Programmierer, der sich auf Echtzeit-Physikoptimierung spezialisiert hat).

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

Die endgültigen Kosten betragen:

nlogn + n + nlogn = n + 2nlogn = O(nlogn) Das ist ziemlich nett.

Hinweis zu RemoveRange:Da wir die Anzahl der Listen nicht festlegen und die Verwendung der Remove-Funktionen vermeiden können, weiß ich nicht genau, wie schnell dieser Vorgang ist, aber ich denke, es ist der schnellste Weg.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top