Hat jemand eine schnelle Methode zum Deduplizieren einer generischen Liste in C #?
ICollection<MyClass> withoutDuplicates = new HashSet<MyClass>(inputList);
Hat jemand eine schnelle Methode zum Deduplizieren einer generischen Liste in C #?
ICollection<MyClass> withoutDuplicates = new HashSet<MyClass>(inputList);
Antworten:
Vielleicht sollten Sie ein HashSet verwenden .
Über den 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 }
*/
HashSet
hat keinen Index , daher ist es nicht immer möglich, ihn zu verwenden. Ich muss einmal eine riesige Liste ohne Duplikate erstellen und sie dann ListView
im virtuellen Modus verwenden. Es war superschnell, HashSet<>
zuerst eine zu erstellen und sie dann in eine umzuwandeln List<>
(so ListView
kann über den Index auf Elemente zugegriffen werden). List<>.Contains()
ist zu langsam.
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 des gleichen Typs:
var noDupes = new HashSet<T>(withDupes);
Oder wenn Sie eine Liste zurückgeben möchten:
var noDupsList = new HashSet<T>(withDupes).ToList();
List<T>
als Ergebnis benötigennew HashSet<T>(withDupes).ToList()
Sortieren Sie es und überprüfen Sie zwei und zwei nebeneinander, da die Duplikate 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:
RemoveAt
ist eine sehr kostspielige Operation an einemList
Ich benutze 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, Geschäftsname, Stadt, Postleitzahl Ich wollte eine Liste der Städte in einer Dropdown-Liste mit doppelten Werten anzeigen. Lösung: Nach Stadt gruppieren und dann die erste für die Liste auswählen.
Ich hoffe, es hilft :)
Es hat bei mir funktioniert. einfach benutzen
List<Type> liIDs = liIDs.Distinct().ToList<Type>();
Ersetzen Sie "Typ" durch Ihren gewünschten Typ, z. B. int.
Wie kronoz in .Net 3.5 sagte, können Sie verwenden Distinct()
.
In .Net 2 können 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 einer Sammlung verwendet werden und gibt die Werte in der ursprünglichen Reihenfolge zurück.
Normalerweise ist es viel schneller, eine Sammlung zu filtern (wie beides Distinct()
und dieses Beispiel), als Elemente daraus zu entfernen.
HashSet
bemerkt, dass der Konstruktor dedupiert hat, was es für die meisten Umstände besser macht. Dies würde jedoch die Sortierreihenfolge beibehalten, was a HashSet
nicht tut.
Dictionary<T, object>
stattdessen ersetzen .Contains
mit .ContainsKey
und .Add(item)
mit.Add(item, null)
HashSet
bleibt die Ordnung erhalten, während Distinct()
dies nicht der Fall ist.
Eine Erweiterungsmethode könnte ein guter Weg sein ... so etwas:
public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
return listToDeduplicate.Distinct().ToList();
}
Und dann rufen Sie zum Beispiel so an:
List<int> myFilteredList = unfilteredList.Deduplicate();
In Java (ich nehme an, C # ist mehr oder weniger identisch):
list = new ArrayList<T>(new HashSet<T>(list))
Wenn Sie die ursprüngliche Liste wirklich mutieren wollten:
List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);
Um die Ordnung zu erhalten, ersetzen Sie einfach HashSet durch LinkedHashSet.
var noDupes = new HashSet<T>(list); list.Clear(); list.AddRange(noDupes);
:)
Dies nimmt verschiedene (die Elemente ohne doppelte Elemente) und konvertiert es wieder in eine Liste:
List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();
Verwenden Sie die Union- Methode von Linq .
Hinweis: Diese Lösung erfordert keine Kenntnisse von Linq, abgesehen davon, dass sie vorhanden ist.
Code
Fügen Sie zunächst Folgendes oben in Ihre Klassendatei ein:
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();
Hinweis: Benennen Sie obj1
in den Namen Ihres Objekts um.
Wie es funktioniert
Der Befehl Union listet jeweils einen Eintrag von zwei Quellobjekten auf. Da obj1 beide Quellobjekte sind, reduziert dies obj1 auf einen von jedem Eintrag.
Das ToList()
gibt eine neue Liste zurück. Dies ist erforderlich, da Linq-Befehle wie Union
das Ergebnis als IEnumerable-Ergebnis zurückgeben, anstatt die ursprüngliche Liste zu ändern oder eine neue Liste zurückzugeben.
Als Hilfsmethode (ohne Linq):
public static List<T> Distinct<T>(this List<T> list)
{
return (new HashSet<T>(list)).ToList();
}
Wenn Sie nicht über die Bestellung kümmern können Sie nur die Einzelteile in einen Schub HashSet
, wenn Sie tun , um die Bestellung erhalten wollen Sie etwas tun können:
var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
if (hs.Add(t))
unique.Add(t);
Oder der Linq-Weg:
var hs = new HashSet<T>();
list.All( x => hs.Add(x) );
Bearbeiten: Die HashSet
Methode ist O(N)
Zeit und O(N)
Raum beim Sortieren und dann einzigartig zu machen (wie von @ lassevk und anderen vorgeschlagen) ist O(N*lgN)
Zeit und O(1)
Raum, daher ist mir (wie auf den ersten Blick) nicht so klar, dass die Sortierweise minderwertig ist (meine Entschuldigung für die vorübergehende Abstimmung ...)
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 die Version von Lasse V. Karlsen, 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);
}
Wenn Sie das MoreLINQ- Paket über Nuget installieren, können Sie die Objektliste leicht anhand einer Eigenschaft unterscheiden
IEnumerable<Catalogue> distinctCatalogues = catalogues.DistinctBy(c => c.CatalogueCode);
Es könnte einfacher sein, einfach sicherzustellen, dass der Liste keine Duplikate hinzugefügt werden.
if(items.IndexOf(new_item) < 0)
items.add(new_item)
List<T>.Contains
Methode jedes Mal, aber mit mehr als 1.000.000 Einträgen. Dieser Prozess verlangsamt meine Bewerbung. Ich benutze List<T>.Distinct().ToList<T>()
stattdessen eine erste.
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 Möglichkeiten, das Problem zu lösen. Das Problem mit den Duplikaten in der folgenden Liste 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 keinen schwer lesbaren LINQ oder 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, die keine zusätzlichen Objekte, Sortierungen usw. erfordert. Sie kann jedoch verbessert werden:
for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)
Die äußere Schleife geht also für die gesamte Liste nach oben, die innere Schleife 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 für die innere Schleife nicht von unten nach oben arbeiten möchten, kann die innere Schleife bei OuterIndex + 1 beginnen.
Alle Antworten kopieren Listen oder erstellen eine neue Liste oder verwenden langsame Funktionen oder sind nur schmerzhaft langsam.
Nach meinem Verständnis ist dies die schnellste und billigste Methode, die ich kenne (auch unterstützt von einem sehr erfahrenen Programmierer, der auf Echtzeit-Physikoptimierung spezialisiert ist).
// 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), was ziemlich nett ist.
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 ausgeführt wird, aber ich denke, dies ist der schnellste Weg.
Wenn Sie zwei Klassen haben Product
und Customer
wir doppelte Elemente aus ihrer Liste entfernen möchten
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 eine generische Klasse im folgenden Formular 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();
dieser Code entfernen doppelte Elemente durch , Id
wenn Sie doppelte Elemente von anderer Eigenschaft wollen entfernen, können Sie ändern , nameof(YourClass.DuplicateProperty)
gleichen nameof(Customer.CustomerName)
dann doppelte Elemente durch Entfernen der CustomerName
Immobilie.
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;
}