Bất cứ ai cũng có một phương pháp nhanh chóng để sao chép lại một Danh sách chung trong C #?
ICollection<MyClass> withoutDuplicates = new HashSet<MyClass>(inputList);
Bất cứ ai cũng có một phương pháp nhanh chóng để sao chép lại một Danh sách chung trong C #?
ICollection<MyClass> withoutDuplicates = new HashSet<MyClass>(inputList);
Câu trả lời:
Có lẽ bạn nên cân nhắc sử dụng Hashset .
Từ liên kết 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
không có chỉ mục , do đó không phải lúc nào cũng có thể sử dụng nó. Tôi phải tạo một lần một danh sách lớn mà không trùng lặp và sau đó sử dụng nó ListView
trong chế độ ảo. Rất nhanh để tạo một cái HashSet<>
đầu tiên và sau đó chuyển đổi nó thành một List<>
(vì vậy ListView
có thể truy cập các mục theo chỉ mục). List<>.Contains()
quá chậm
Nếu bạn đang sử dụng .Net 3+, bạn có thể sử dụng Linq.
List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();
Làm thế nào về:
var noDupes = list.Distinct().ToList();
Trong .net 3.5?
Đơn giản chỉ cần khởi tạo Hashset với Danh sách cùng loại:
var noDupes = new HashSet<T>(withDupes);
Hoặc, nếu bạn muốn Danh sách được trả về:
var noDupsList = new HashSet<T>(withDupes).ToList();
List<T>
sử dụng kết quảnew HashSet<T>(withDupes).ToList()
Sắp xếp nó, sau đó kiểm tra hai và hai bên cạnh nhau, vì các bản sao sẽ co cụm lại với nhau.
Một cái gì đó như thế này:
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--;
}
Ghi chú:
RemoveAt
là một hoạt động rất tốn kém trên mộtList
Tôi thích sử dụng lệnh này:
List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
.GroupBy(s => s.City)
.Select(grp => grp.FirstOrDefault())
.OrderBy(s => s.City)
.ToList();
Tôi có các trường này trong danh sách của mình: Id, StoreName, City, PostalCode Tôi muốn hiển thị danh sách các thành phố trong danh sách thả xuống có các giá trị trùng lặp. Giải pháp: Nhóm theo thành phố sau đó chọn cái đầu tiên cho danh sách.
Tôi hy vọng nó sẽ giúp :)
Nó làm việc cho tôi. chỉ cần sử dụng
List<Type> liIDs = liIDs.Distinct().ToList<Type>();
Thay thế "Loại" bằng loại mong muốn của bạn, ví dụ int.
Như kronoz đã nói trong .Net 3.5, bạn có thể sử dụng Distinct()
.
Trong .Net 2 bạn có thể bắt chước nó:
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;
}
Điều này có thể được sử dụng để khấu trừ bất kỳ bộ sưu tập nào và sẽ trả về các giá trị theo thứ tự ban đầu.
Việc lọc một bộ sưu tập (như cả hai Distinct()
và mẫu này đều nhanh hơn) sẽ nhanh hơn nhiều so với việc loại bỏ các mục khỏi nó.
HashSet
xây dựng đã khấu trừ, điều này làm cho nó tốt hơn cho hầu hết các trường hợp. Tuy nhiên, điều này sẽ duy trì thứ tự sắp xếp, mà HashSet
không.
Dictionary<T, object>
thay vào đó, thay thế .Contains
với .ContainsKey
và .Add(item)
với.Add(item, null)
HashSet
duy trì trật tự trong khi Distinct()
không.
Một phương thức mở rộng có thể là một cách hay để đi ... một cái gì đó như thế này:
public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
return listToDeduplicate.Distinct().ToList();
}
Và sau đó gọi như thế này, ví dụ:
List<int> myFilteredList = unfilteredList.Deduplicate();
Trong Java (tôi giả sử C # giống hoặc ít hơn):
list = new ArrayList<T>(new HashSet<T>(list))
Nếu bạn thực sự muốn thay đổi danh sách ban đầu:
List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);
Để duy trì trật tự, chỉ cần thay thế Hashset bằng LinkedHashset.
var noDupes = new HashSet<T>(list); list.Clear(); list.AddRange(noDupes);
:)
Điều này có sự khác biệt (các yếu tố không có các yếu tố trùng lặp) và chuyển đổi nó thành một danh sách một lần nữa:
List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();
Sử dụng phương pháp Liên minh của Linq .
Lưu ý: Giải pháp này không đòi hỏi kiến thức về Linq, ngoài việc nó tồn tại.
Mã
Bắt đầu bằng cách thêm phần sau vào đầu tệp lớp của bạn:
using System.Linq;
Bây giờ, bạn có thể sử dụng cách sau để xóa các bản sao khỏi một đối tượng được gọi là obj1
:
obj1 = obj1.Union(obj1).ToList();
Lưu ý: Đổi tên obj1
thành tên của đối tượng của bạn.
Làm thế nào nó hoạt động
Lệnh Union liệt kê một trong mỗi mục của hai đối tượng nguồn. Vì obj1 là cả hai đối tượng nguồn, điều này làm giảm obj1 xuống một trong mỗi mục.
Trả ToList()
về một danh sách mới. Điều này là cần thiết, bởi vì các lệnh Linq như Union
trả về kết quả dưới dạng kết quả IEnountable thay vì sửa đổi Danh sách gốc hoặc trả về Danh sách mới.
Là một phương thức trợ giúp (không có Linq):
public static List<T> Distinct<T>(this List<T> list)
{
return (new HashSet<T>(list)).ToList();
}
Nếu bạn không quan tâm đến thứ tự bạn chỉ có thể xô các mục vào một HashSet
, nếu bạn làm muốn duy trì trật tự bạn có thể làm một cái gì đó như thế này:
var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
if (hs.Add(t))
unique.Add(t);
Hoặc cách Linq:
var hs = new HashSet<T>();
list.All( x => hs.Add(x) );
Chỉnh sửa: Các HashSet
phương pháp là O(N)
thời gian và O(N)
không gian trong khi phân loại và sau đó làm duy nhất (theo đề nghị của @ lassevk và những người khác) là O(N*lgN)
thời gian và O(1)
không gian vì vậy nó không quá rõ ràng với tôi (vì nó là ở cái nhìn đầu tiên) rằng cách sắp xếp là kém hơn (tôi xin lỗi vì đã bỏ phiếu tạm thời ...)
Đây là một phương pháp mở rộng để loại bỏ các bản sao liền kề tại chỗ. Gọi Sắp xếp () trước và vượt qua trong cùng một IComparer. Điều này sẽ hiệu quả hơn phiên bản của Lasse V. Karlsen, liên tục gọi RemoveAt (dẫn đến việc di chuyển nhiều bộ nhớ khối).
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);
}
Có thể dễ dàng hơn để đảm bảo rằng các bản sao không được thêm vào danh sách.
if(items.IndexOf(new_item) < 0)
items.add(new_item)
List<T>.Contains
phương pháp này mỗi lần nhưng với hơn 1.000.000 mục. Quá trình này làm chậm ứng dụng của tôi. Tôi đang sử dụng một List<T>.Distinct().ToList<T>()
thay thế đầu tiên.
Một cách khác trong .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();
}
Có nhiều cách để giải quyết - vấn đề trùng lặp trong Danh sách, dưới đây là một trong số đó:
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);
}
}
Chúc mừng Ravi Ganesan
Đây là một giải pháp đơn giản không yêu cầu bất kỳ LINQ khó đọc nào hoặc bất kỳ sự sắp xếp nào trước đó của danh sách.
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
}
}
}
}
Câu trả lời của David J. là một phương pháp tốt, không cần thêm đối tượng, sắp xếp, v.v. Tuy nhiên, nó có thể được cải thiện:
for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)
Vì vậy, vòng lặp bên ngoài đi xuống dưới cùng cho toàn bộ danh sách, nhưng vòng lặp bên trong đi xuống dưới cùng "cho đến khi đạt được vị trí vòng lặp bên ngoài".
Vòng lặp bên ngoài đảm bảo toàn bộ danh sách được xử lý, vòng lặp bên trong tìm thấy các bản sao thực tế, những điều đó chỉ có thể xảy ra trong phần mà vòng lặp bên ngoài chưa được xử lý.
Hoặc nếu bạn không muốn thực hiện từ dưới lên cho vòng lặp bên trong, bạn có thể bắt đầu vòng lặp bên trong ở ngoài Index + 1.
Tất cả các câu trả lời sao chép danh sách, hoặc tạo một danh sách mới, hoặc sử dụng các chức năng chậm hoặc chỉ chậm một cách đau đớn.
Theo hiểu biết của tôi, đây là phương pháp nhanh nhất và rẻ nhất mà tôi biết (cũng được hỗ trợ bởi một lập trình viên rất có kinh nghiệm chuyên về tối ưu hóa vật lý thời gian thực).
// 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();
Chi phí cuối cùng là:
nlogn + n + nlogn = n + 2nlogn = O (nlogn) khá đẹp.
Lưu ý về RemoveRange: Vì chúng tôi không thể thiết lập số lượng của danh sách và tránh sử dụng chức năng Xóa, tôi không biết chính xác tốc độ của thao tác này nhưng tôi đoán đó là cách nhanh nhất.
Nếu bạn có các lớp học kéo Product
và Customer
và chúng tôi muốn xoá các mục trùng lặp khỏi danh sách của họ
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; }
}
Bạn phải định nghĩa một lớp chung trong mẫu dưới đây
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();
}
}
sau đó, bạn có thể xóa các mục trùng lặp trong danh sách của bạn.
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();
mã này xóa các mục trùng lặp bằng cách Id
nếu bạn muốn xóa các mục trùng lặp bởi thuộc tính khác, bạn có thể thay đổi nameof(YourClass.DuplicateProperty)
tương tự nameof(Customer.CustomerName)
sau đó xóa các mục trùng lặp theo CustomerName
Thuộc tính.
Một cách thực hiện trực quan đơn giản:
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;
}