2010-02-01 43 views
11

Tôi nghĩ rằng tôi đã hiểu được Intersect, nhưng hóa ra tôi đã sai.Tìm tất cả dữ liệu giao nhau, không chỉ các giá trị duy nhất

List<int> list1 = new List<int>() { 1, 2, 3, 2, 3}; 
List<int> list2 = new List<int>() { 2, 3, 4, 3, 4}; 

list1.Intersect(list2) =>  2,3 

//But what I want is: 
// => 2,3,2,3,2,3,3 

tôi có thể hình dung một cách như:

var intersected = list1.Intersect(list2); 
var list3 = new List<int>(); 
list3.AddRange(list1.Where(I => intersected.Contains(I))); 
list3.AddRange(list2.Where(I => intersected.Contains(I))); 

Có cách nào dễ dàng hơn trong LINQ để đạt được điều này?

Tôi cần nêu rõ rằng tôi không quan tâm đến thứ tự các kết quả được đưa ra.

2,2,2,3,3,3,3 cũng sẽ hoàn toàn OK.

Vấn đề là tôi đang sử dụng bộ sưu tập này trên một bộ sưu tập rất lớn, vì vậy tôi cần hiệu quả.

Chúng tôi đang nói về Đối tượng chứ không phải int. Ints chỉ là một ví dụ đơn giản, nhưng tôi nhận ra điều này có thể tạo nên sự khác biệt.

+0

Với bản cập nhật của bạn, thậm chí có thể có nhiều cách hiệu quả hơn để giải quyết vấn đề của bạn. Hãy cho chúng tôi biết thêm về dữ liệu. Cụ thể, tôi quan tâm đến câu hỏi liệu bộ sưu tập rất lớn của bạn có phần lớn các yếu tố độc đáo hay chủ yếu là sao chép hay không. Tôi cũng quan tâm để biết liệu các phần tử này có thực sự là số nguyên hay không, hoặc nếu đây là một phần tử quan trọng đối với một số loại phức tạp hơn; cụ thể, có tổng số * đặt hàng * được xác định trên dữ liệu của bạn không? Tức là, được đưa ra một tập hợp dữ liệu này, có một thứ tự từ nhỏ đến lớn nhất được xác định rõ ràng không? –

Trả lời

15

Hãy xem chúng tôi có thể mô tả chính xác những gì bạn muốn không. Đúng nếu tôi đã sai lầm. Bạn muốn: tất cả các phần tử của danh sách 1, theo thứ tự, cũng xuất hiện trong danh sách 2, tiếp theo là tất cả các phần tử của danh sách 2, theo thứ tự, cũng xuất hiện trong danh sách 1. Có?

Có vẻ đơn giản.

return list1.Where(x=>list2.Contains(x)) 
    .Concat(list2.Where(y=>list1.Contains(y))) 
    .ToList(); 

Lưu ý rằng đây là không hiệu quả cho danh sách lớn. Nếu danh sách có hàng nghìn mục thì điều này có một vài triệu so sánh. Nếu bạn đang ở trong tình huống đó thì bạn muốn sử dụng cấu trúc dữ liệu hiệu quả hơn để thử nghiệm thành viên:

list1set = new HashSet(list1); 
list2set = new HashSet(list2); 

return list1.Where(x=>list2set.Contains(x)) 
    .Concat(list2.Where(y=>list1set.Contains(y))) 
    .ToList(); 

chỉ thực hiện một vài nghìn so sánh, nhưng có khả năng sử dụng nhiều bộ nhớ hơn.

+5

Truy vấn LINQ của bạn không cho kết quả tương tự như hai truy vấn khác của bạn - nếu phần tử e xảy ra n lần trong list1 và m trong list2, chúng chứa n * m lần, không phải là hành vi mong muốn. – kvb

+2

* Bắt tuyệt vời * @kvb. Tôi hoàn toàn bỏ lỡ điều đó bởi vì trong ví dụ đã cho, chúng có vẻ tương tự gây nhầm lẫn. Tôi sẽ xóa mã không chính xác. Cảm ơn! –

+0

Thú vị về HashSet. Tôi không biết rằng nó hiệu quả hơn. Sẽ nhìn vào nó! – Peterdk

-1

Tôi không tin điều này là có thể với các API tích hợp. Nhưng bạn có thể sử dụng những điều sau đây để có được kết quả mà bạn đang tìm kiếm.

IEnumerable<T> Intersect2<T>(this IEnumerable<T> left, IEnumerable<T> right) { 
    var map = left.ToDictionary(x => x, y => false); 
    foreach (var item in right) { 
    if (map.ContainsKey(item)) { 
     map[item] = true; 
    } 
    } 
    foreach (var cur in left.Concat(right)) { 
    if (map.ContainsKey(cur)) { 
     yield return cur; 
    } 
    } 
} 
1
var set = new HashSet(list1.Intersect(list2)); 
return list1.Concat(list2).Where(i=>set.Contains(i)); 
Các vấn đề liên quan