Bất kỳ ý tưởng về cách kiểm tra xem danh sách đó là một tập hợp con khác?
Cụ thể, tôi có
List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };
Làm cách nào để kiểm tra t2 là tập con của t1, sử dụng LINQ?
Bất kỳ ý tưởng về cách kiểm tra xem danh sách đó là một tập hợp con khác?
Cụ thể, tôi có
List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };
Làm cách nào để kiểm tra t2 là tập con của t1, sử dụng LINQ?
Câu trả lời:
bool isSubset = !t2.Except(t1).Any();
Sử dụng Hashset thay vì List nếu làm việc với các bộ. Sau đó, bạn có thể chỉ cần sử dụng IsSubsetOf ()
HashSet<double> t1 = new HashSet<double>{1,3,5};
HashSet<double> t2 = new HashSet<double>{1,5};
bool isSubset = t2.IsSubsetOf(t1);
Xin lỗi vì nó không sử dụng LINQ. :-(
Nếu bạn cần sử dụng danh sách, thì giải pháp của @ Jared sẽ hoạt động với lời cảnh báo rằng bạn sẽ cần xóa mọi phần tử lặp lại tồn tại.
Nếu bạn đang kiểm tra đơn vị, bạn cũng có thể sử dụng phương thức CollectionAssert.IsSubsetOf :
CollectionAssert.IsSubsetOf(subset, superset);
Trong trường hợp trên, điều này có nghĩa là:
CollectionAssert.IsSubsetOf(t2, t1);
Đây là một giải pháp hiệu quả hơn đáng kể so với các giải pháp khác được đăng ở đây, đặc biệt là giải pháp hàng đầu:
bool isSubset = t2.All(elem => t1.Contains(elem));
Nếu bạn có thể tìm thấy một phần tử duy nhất trong t2 không có trong t1, thì bạn biết rằng t2 không phải là tập con của t1. Ưu điểm của phương pháp này là nó được thực hiện tại chỗ, không phân bổ không gian bổ sung, không giống như các giải pháp sử dụng .Except hoặc .Intersect. Hơn nữa, giải pháp này có thể phá vỡ ngay khi tìm thấy một yếu tố duy nhất vi phạm điều kiện tập hợp con, trong khi các giải pháp khác tiếp tục tìm kiếm. Dưới đây là hình thức dài tối ưu của giải pháp, chỉ nhanh hơn một chút trong các thử nghiệm của tôi so với giải pháp tốc ký trên.
bool isSubset = true;
foreach (var element in t2) {
if (!t1.Contains(element)) {
isSubset = false;
break;
}
}
Tôi đã làm một số phân tích hiệu suất thô sơ của tất cả các giải pháp, và kết quả rất quyết liệt. Hai giải pháp này nhanh hơn khoảng 100 lần so với các giải pháp .Except () và .Intersect () và không sử dụng thêm bộ nhớ.
!t2.Except(t1).Any()
là những gì đang làm. Linq đang làm việc trở lại. Any()
đang hỏi IEnumerable
nếu có ít nhất một yếu tố. Trong kịch bản t2.Except(t1)
này chỉ phát ra phần tử đầu tiên t2
không có trong đó t1
. Nếu phần tử đầu tiên t2
không có trong t1
nó kết thúc nhanh nhất, nếu tất cả các phần tử t2
nằm trong t1
nó chạy lâu nhất.
t1={1,2,3,...9999}
và t2={9999,9998,99997...9000}
, bạn có được các phép đo sau : !t2.Except(t1).Any(): 1ms -> t2.All(e => t1.Contains(e)): 702ms
. Và nó càng tệ hơn thì phạm vi càng lớn.
t2.Except (t1)
đang trở về IEnumerable
không a Collection
. Nó chỉ phát ra tất cả các mục có thể nếu bạn lặp hoàn toàn trên nó, ví dụ bằng cách ToArray ()
hoặc ToList ()
hoặc sử dụng foreach
mà không vi phạm bên trong. Tìm kiếm linq hoãn thực thi để đọc thêm về khái niệm đó.
t2={1,2,3,4,5,6,7,8}
t1={2,4,6,8}
t2.Except(t1)
=> phần tử đầu tiên của t2 = 1 => chênh lệch 1 đến t1 là 1 (được kiểm tra so với {2,4,6,8}) => Except()
phát ra phần tử đầu tiên 1 => Any()
lấy phần tử => Any()
kết quả là true => không kiểm tra thêm các phần tử trong t2.
@ Giải pháp của Cameron như một phương pháp mở rộng:
public static bool IsSubsetOf<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
return !a.Except(b).Any();
}
Sử dụng:
bool isSubset = t2.IsSubsetOf(t1);
(Điều này tương tự, nhưng không hoàn toàn giống với bài đăng trên blog của @ Michael)
Dựa trên các câu trả lời từ @Cameron và @Neil, tôi đã viết một phương thức mở rộng sử dụng thuật ngữ tương tự như lớp Enumerable.
/// <summary>
/// Determines whether a sequence contains the specified elements by using the default equality comparer.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <param name="source">A sequence in which to locate the values.</param>
/// <param name="values">The values to locate in the sequence.</param>
/// <returns>true if the source sequence contains elements that have the specified values; otherwise, false.</returns>
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> values)
{
return !values.Except(source).Any();
}
Thử cái này
static bool IsSubSet<A>(A[] set, A[] toCheck) {
return set.Length == (toCheck.Intersect(set)).Count();
}
Ý tưởng ở đây là Intersect sẽ chỉ trả về các giá trị trong cả Mảng. Tại thời điểm này nếu độ dài của tập kết quả giống với tập hợp ban đầu, thì tất cả các phần tử trong "tập hợp" cũng nằm trong "kiểm tra" và do đó "tập hợp" là tập hợp con của "toCheck"
Lưu ý: Giải pháp của tôi không hoạt động nếu "bộ" có trùng lặp. Tôi không thay đổi vì tôi không muốn lấy cắp phiếu bầu của người khác.
Gợi ý: Tôi đã bỏ phiếu cho câu trả lời của Cameron.