2009-03-10 36 views
31

Có phương pháp linq được xây dựng trong tôi có thể sử dụng để tìm hiểu xem hai chuỗi có chứa các mục giống nhau hay không, không tính đến thứ tự?C#: So sánh nội dung của hai IEnumerables

Ví dụ:

{1, 2, 3} == {2, 1, 3} 
{1, 2, 3} != {2, 1, 3, 4} 
{1, 2, 3} != {1, 2, 4} 

Bạn có SequenceEquals, nhưng sau đó tôi sẽ phải theo thứ tự cả hai trình tự đầu tiên, sẽ không?

+0

bản sao có thể có của [So sánh hai bộ sưu tập cho bình đẳng bất kể thứ tự của các mục trong chúng] (http://stackoverflow.com/questions/50098/comparing-two-collections-for-equality-irrespective-of-the- order-of-items-in-the) – nawfal

Trả lời

31

Có một vài cách. Giả sử A và B là IEnumerable.

!A.Except(B).Any() && !B.Except(A).Any() 
A.Count() == B.Count() && A.Intersect(B).Count() == B.Count() 
etc 
+0

sử dụng Intersect ở đó, bạn có cần thực hiện cả hai cách không? – Svish

+0

Tôi nghĩ rằng điều này là chậm hơn nhiều so với đặt hàng các trình tự. Trừ khi bạn đang đối phó với cây biểu hiện. –

+0

@Svish: Có, bạn * làm * cần thực hiện cả hai cách A = B iff A Δ B = Φ. –

1

Tôi nghĩ rằng sắp xếp thứ tự là cách nhanh nhất bạn có thể đạt được điều này.

+0

Đối với SequenceEquals, nó phải có cùng thứ tự. – leppie

+0

"Trình tự SequenceEqual <(Of <(TSource>)>) (IEnumerable <(Of <(TSource>)>), IEnumerable <(Of <(TSource>)>)) liệt kê hai chuỗi nguồn song song và so sánh các phần tử tương ứng bằng cách sử dụng trình so sánh bình đẳng mặc định cho TSource , Mặc định." – Svish

+0

Vâng, tôi đã đọc sai câu hỏi. Đây là O (nlogn) là thời gian tiệm cận nhanh nhất mà thuật toán có thể đạt được cho mục đích này. –

1

Tôi đã làm điều này cho việc sáp nhập các mặt hàng mới vào một bộ sưu tập mà không cần bản sao, phải mất hai bộ sưu tập và trả về tất cả các mục có ra bất kỳ bản sao

List<Campaign> nonMatching = (from n in newCampaigns 
where !(from e in Existing select e.Id).Contains<int>(n.Id) 
select n).ToList<Campaign>(); 

Bây giờ bằng cách loại bỏ! cho chứa tuyên bố

List<Campaign> nonMatching = (from n in newCampaigns 
where (from e in Existing select e.Id).Contains<int>(n.Id) 
select n).ToList<Campaign>(); 

nó sẽ trở lại các bản sao

0

Nếu bạn đang thực sự chỉ là thử nghiệm để xem nếu có trùng lắp, sau đó đề nghị leppie của nên làm việc:

if (A.Except(B).Count == 0 && B.Except(A).Count == 0) {...} 

Nhưng nếu bạn chỉ cần đến một IEnumerable mà không có bản sao:

var result = A.Union(B).Distinct(); 
3

Hãy thử HashSet c lass:

var enumA = new[] { 1, 2, 3, 4 }; 
var enumB = new[] { 4, 3, 1, 2 }; 

var hashSet = new HashSet<int>(enumA); 
hashSet.SymmetricExceptWith(enumB); 
Console.WriteLine(hashSet.Count == 0); //true => equal 

Nhưng điều đó chỉ hoạt động chính xác nếu các giá trị khác biệt.

Ví dụ

var enumA = new[] { 1, 1, 1, 2 }; 
var enumB = new[] { 1, 2, 2, 2 }; 

cũng được coi là "bình đẳng" với các phương pháp đã đề cập.

+1

'return hashSet.SetEquals (enumB);' – Spook

8

Với hai IEnumerables (A và B):

bool equal = (A.Count() == B.Count() && (!A.Except(B).Any() || !B.Except(A).Any())) 

Tôi nghĩ rằng đây là tốt hơn so với Trừ (A) Count vì toàn bộ excep sẽ không được đánh giá. Nó sẽ dừng lại ngay sau khi một phần tử được tìm thấy trong Ngoại trừ. Với Đếm, toàn bộ Ngoại trừ được đánh giá. Ngày đầu này, chúng ta có thể tránh việc đánh giá những chi phí này Ngoại trừ chỉ bằng cách kiểm tra các thuộc tính Count đầu tiên. Nếu Đếm không bằng nhau, thì chúng ta kiểm tra Excepts.

+0

Tình yêu mà Ngoại trừ sẽ mang lại sự khác biệt đầu tiên - tốt đẹp! – U007D

2

Nếu bạn không quan tâm đến bản sao (ví dụ: bạn muốn xem xét {1, 2, 3} được bằng {1, 2, 3, 2}) thì:

new HashSet<int>(A).SetEquals(B) 

(Hoặc bất cứ loại là yếu tố gõ thay vì int).

Nếu không:

public static bool SequenceEqualUnordered<T>(IEnumerable<T> first, IEnumerable<T> second) 
{ 
    if (first == null) 
     return second == null; // or throw if that's more appropriate to your use. 
    if (second == null) 
     return false; // likewise. 
    var dict = new Dictionary<T, int>(); // You could provide a IEqualityComparer<T> here if desired. 
    foreach(T element in first) 
    { 
     int count; 
     dict.TryGetValue(element, out count); 
     dict[element] = count + 1; 
    } 
    foreach(T element in second) 
    { 
     int count; 
     if (!dict.TryGetValue(element, out count)) 
      return false; 
     else if (--count == 0) 
      dict.Remove(element); 
     else 
      dict[element] = count; 
    } 
    return dict.Count == 0; 
} 

Giữ một kiểm đếm của mỗi phần tử trong dãy đầu tiên, sau đó kiểm tra thứ hai chống lại nó. Thời điểm bạn có quá nhiều thứ tự trong chuỗi thứ hai, bạn có thể trả về false, nếu không bạn không còn gì trong từ điển của các số đó bằng nhau hoặc giả nếu có bất kỳ phần tử nào còn lại. Không phải là hai loại O (n log n) sử dụng OrderBy() theo sau so sánh O (n), bạn đã có một hoạt động O (n) xây dựng bộ số đo và kiểm tra O (n) chống lại nó.