Відповіді:
Можливо, вам варто подумати про використання 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 }
*/
HashSet
не має індексу , тому використовувати його не завжди можливо. Я повинен створити один раз величезний список без дублікатів, а потім використовувати його ListView
у віртуальному режимі. Зробити HashSet<>
перший було дуже швидко, а потім перетворити його в List<>
(так ListView
можна отримати доступ до елементів за індексом). List<>.Contains()
занадто повільно.
Якщо ви використовуєте .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<T>
як результат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--;
}
Примітки:
RemoveAt
це дуже дорога операція наList
Мені подобається використовувати цю команду:
List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
.GroupBy(s => s.City)
.Select(grp => grp.FirstOrDefault())
.OrderBy(s => s.City)
.ToList();
У мене в списку є такі поля: Id, StoreName, City, PostalCode. Я хотів показати список міст у спадному меню, що має повторювані значення. рішення: Групуйте по містах, тоді виберіть перший для списку.
Я сподіваюся, що це допомагає :)
Це працювало для мене. просто використовувати
List<Type> liIDs = liIDs.Distinct().ToList<Type>();
Замініть "Тип" на потрібний тип, наприклад, int.
Як сказав 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;
}
Це може бути використане для виведення будь-якої колекції і поверне значення у вихідному порядку.
Зазвичай фільтрувати колекцію (як Distinct()
і цей зразок) зазвичай набагато швидше, ніж було б видалити з неї предмети.
HashSet
конструктор виводився, що робить його кращим для більшості обставин. Однак це збереже порядок сортування, якого HashSet
немає.
Dictionary<T, object>
замість цього, замінити .Contains
на .ContainsKey
і .Add(item)
з.Add(item, null)
HashSet
зберігає порядок, а Distinct()
ні.
Метод розширення може бути гідним шляхом ... щось подібне:
public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
return listToDeduplicate.Distinct().ToList();
}
А потім дзвоніть так, наприклад:
List<int> myFilteredList = unfilteredList.Deduplicate();
У 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.
var noDupes = new HashSet<T>(list); list.Clear(); list.AddRange(noDupes);
:)
Це відрізняє (елементи без дублювання елементів) і знову перетворює їх у список:
List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();
Використовуйте метод Linq's Union .
Примітка: для цього рішення не потрібно знати Linq, окрім того, що воно існує.
Код
Почніть з додавання наступного у верхній частині вашого класу:
using System.Linq;
Тепер ви можете скористатись наступним для видалення дублікатів із об’єкта, який називається obj1
:
obj1 = obj1.Union(obj1).ToList();
Примітка: Перейменуйте obj1
на ім'я вашого об'єкта.
Як це працює
Команда Union перераховує один з кожного запису двох вихідних об'єктів. Оскільки obj1 є обома вихідними об'єктами, це зводить obj1 до одного з кожного запису.
ToList()
Повертає новий список. Це необхідно, тому що команди команд Linq як Union
повертають результат як результат IEnumerable замість зміни оригінального списку або повернення нового списку.
Як допоміжний метод (без Linq):
public static List<T> Distinct<T>(this List<T> list)
{
return (new HashSet<T>(list)).ToList();
}
Якщо ви не дбаєте про порядок ви можете просто засунути елементи в 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)
простору у час сортування , а потім зробити унікальний (як це було запропоновано @ lassevk і інші) це O(N*lgN)
час і O(1)
простір , так що це не так ясно для мене (як це було на перший погляд) , що сортування шлях поступається (мій вибачення за тимчасове голосування проти
Ось метод розширення для видалення сусідніх дублікатів in situ. Спершу зателефонуйте Сортувати () та передайте у цьому ж 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);
}
Можливо, буде простіше просто переконатися, що дублікати не додано до списку.
if(items.IndexOf(new_item) < 0)
items.add(new_item)
List<T>.Contains
метод щоразу, але з більш ніж 1 000 000 записів. Цей процес уповільнює мою програму. Я використовую List<T>.Distinct().ToList<T>()
перший замість цього.
Ще один спосіб у .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--)
Таким чином, зовнішній цикл йде у верхній нижній частині для всього списку, а внутрішній цикл йде внизу, «поки не буде досягнуто положення зовнішньої петлі».
Зовнішній цикл гарантує, що весь список оброблений, внутрішній цикл знаходить фактичні дублікати, це може відбуватися лише в тій частині, яку зовнішній цикл ще не обробив.
Або якщо ви не хочете робити знизу вгору для внутрішнього циклу, ви можете почати внутрішній цикл у externalIndex + 1.
Усі відповіді копіюють списки, або створюють новий список, або використовують повільні функції, або просто болісно повільні.
Наскільки я розумію, це найшвидший і найдешевший метод, який я знаю (також підтримується дуже досвідченим програмістом, який спеціалізується на оптимізації фізики в реальному часі).
// 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 = O (nlogn), що досить приємно.
Примітка про RemoveRange: Оскільки ми не можемо встановити кількість списку та уникати використання функцій Remove, я не знаю точно швидкості цієї операції, але, мабуть, це найшвидший спосіб.
Якщо у вас є класи буксирних 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;
}