2013-02-09 31 views
5

Tôi có hai trường hợp loại IEnumerable như sau.Lấy một giao điểm của hai IEnumerables bằng LINQ

IEnumerable<Type1> type1 = ...; 
IEnumerable<Type2> type2 = ...; 

Cả Type1Type2 chứa một thành viên gọi là common, vì vậy mặc dù họ là các lớp khác nhau, chúng tôi vẫn có thể liên hệ chúng như thế này.

type1[0].common == type2[4].common 

Tôi đang cố gắng để lọc bỏ những yếu tố của type1 rằng không có common giá trị tương ứng trong type2 và tạo một từ điển dựa trên một giá trị từ mỗi. Ngay bây giờ, tôi làm điều đó bằng vòng lặp đôi sau đây.

Dictionary<String, String> intersection = ...; 
foreach (Type1 t1 in type1) 
    foreach(Type2 t2 in type2) 
    if (t1.common == t2.common) 
     intersection.Add(t1.Id, t2.Value); 

Bây giờ, tôi đã thử với LINQ nhưng tất cả các .Where, .Select.ForEach chỉ cho tôi đau đầu. Có cách nào để gọn gàng thực hiện các hoạt động tương tự bằng cách sử dụng LINQ?

+0

LINQ sẽ chỉ liệt kê mọi thứ. Bạn có vấn đề về hiệu suất không? – Paparazzi

+0

Không. Nhưng những người lập trình khác đang cười với tôi. :(Họ nói rằng tôi mệt mỏi để tìm mã và điều đó là không thể. Trong khi tôi có thể thừa nhận điều đầu tiên, tôi từ chối đồng ý với điều sau. :) –

+1

Vậy điều gì sẽ xảy ra khi bạn có một t1 có * hai * t2s trận đấu đó? –

Trả lời

14

Khi hai chuỗi có một điểm chung và bạn muốn lọc sản phẩm của họ dựa trên sự tương đồng đó, các truy vấn hiệu quả là một tham gia. Giả sử Loại1Khách hàngLoại2Đặt hàng. Mọi khách hàng đều có CustomerID và mọi đơn hàng cũng có CustomerID. Sau đó, bạn có thể nói điều này.

var query = from customer in customers 
      join order in orders 
       on customer.CustomerId equals order.CustomerId 
      select new { customer.Name, order.Product }; 

Lặp lại điều đó sẽ cung cấp cho bạn chuỗi các cặp bao gồm mọi tên khách hàng có đơn đặt hàng và tất cả các sản phẩm của họ. Vì vậy, nếu khách hàng Suzy ra lệnh cho một chiếc bánh và một chiếc bánh pizza và khách hàng Bob gọi món bít-tết, bạn sẽ nhận được những cặp này.

Suzy, pancake 
Suzy, pizza 
Bob, steak 

Nếu thay vào đó bạn muốn nhóm được nhóm lại để mọi khách hàng có danh sách đơn hàng của họ, đó là một nhóm tham gia.

var query = from customer in customers 
      join order in orders 
       on customer.CustomerId equals order.CustomerId 
       into products 
      select new { customer.Name, products }; 

Lặp lại cung cấp cho bạn cặp mục đầu tiên là tên và mục thứ hai là một chuỗi sản phẩm.

Suzy, { pancake, pizza } 
Bob, { steak } 
+0

Vàng nguyên chất, hihi! Trong thời gian này, cách tiếp cận đầu tiên chỉ là những gì tôi cần (tôi không biết làm thế nào tôi không thể thiết lập nó lên bản thân mình). Nhưng người othe thì mới đối với tôi. Không biết về nhóm nhưng nó ** sẽ có ích khá sớm. –

+0

Tôi nghĩ rằng việc đăng các cuộc gọi phương thức mở rộng tương đương ngoài biểu thức truy vấn sẽ làm cho việc hiểu điều gì xảy ra dễ dàng hơn và đặc điểm hiệu suất là gì. – CodesInChaos

-1
type1.where(i=>type2.where(j=>j.common == i.common).Count > 0); 

Điều này sẽ giúp bạn có được danh sách chỉ những người phù hợp.

+0

Điều này khá hiệu quả hơn một chút so với 'Tham gia'. Bạn đang thực hiện rất nhiều lần lặp của 'type2' ở đây thông qua các tìm kiếm tuyến tính trái với việc thực hiện các hoạt động dựa trên tập hợp. Bạn cũng không parinig mục 't1' với bạn tình trong' type2'. – Servy

1

Một tùy chọn khác sẽ được tham gia. Tôi đã làm một ứng dụng giao diện điều khiển nhanh bên dưới, nhưng phải tạo nên dữ liệu của riêng tôi. Hy vọng tôi hiểu câu hỏi của bạn một cách chính xác.

public class Type1 
{ 
    public string ID { get; set; } 
    public Guid common { get; set; } 
} 
public class Type2 
{ 
    public string Value { get; set; } 
    public Guid common { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Guid CommonGuid = Guid.NewGuid(); 

     IEnumerable<Type1> EnumType1 = new List<Type1>() 
     { 
      new Type1() { 
       ID = "first", 
       common = CommonGuid 
      }, 
      new Type1() { 
       ID = "second", 
       common = CommonGuid 
      }, 
      new Type1() { 
       ID = "third", 
       common = Guid.NewGuid() 
      } 
     } as IEnumerable<Type1>; 

     IEnumerable<Type2> EnumType2 = new List<Type2>() 
     { 
      new Type2() { 
       Value = "value1", 
       common = CommonGuid 
      }, 
      new Type2() { 
       Value = "value2", 
       common = Guid.NewGuid() 
      }, 
      new Type2() { 
       Value = "value3", 
       common = CommonGuid 
      } 
     } as IEnumerable<Type2>; 

     //--The part that matters 
     EnumType1      //--First IEnumerable 
      .Join(      //--Command 
       EnumType2,    //--Second IEnumerable 
       outer => outer.common, //--Key to join by from EnumType1 
       inner => inner.common, //--Key to join by from EnumType2 
       (inner, outer) => new { ID = inner.ID, Value = outer.Value }) //--What to do with matching "rows" 
      .ToList() //--Not necessary, just used so that I can use the foreach below 
      .ForEach(item => 
       { 
        Console.WriteLine("{0}: {1}", item.ID, item.Value); 
       }); 

     Console.ReadKey(); 
    } 
} 

trưng bày dưới đây:
đầu tiên: value1
đầu tiên: value3
thứ hai: value1
thứ hai: value3

+0

Yupp. Ví dụ tốt - rất rõ ràng và mang tính thông tin. Tôi quản lý để rút ngắn hoạt động cho một chỉ bốn dòng LINQ với nó như là một nhảy-off-điểm. –

0

Giả sử rằng bạn vẫn muốn giữ lại các ngã tư như một Dictionary<string, string>:

IEnumerable<Type1> list1; 
IEnumerable<Type2> list2; 

Dictionary<string, string> intersection = 
    (from item1 in list1 
    from item2 in list2 
    where item1.common = item2.common 
    select new { Key = item1.Id, Value = item2.Value }) 
     .ToDictionary(x => x.Key, x => x.Value); 
+0

Điều này khá hiệu quả hơn một chút so với 'Tham gia'. Bạn đang tạo ra rất nhiều cặp và sau đó lọc chúng, trái ngược với một tham gia mà chỉ là tạo ra tất cả các cặp ngay từ đầu. – Servy

-1

Tôi bị thiếu cái gì đó, nhưng woul d làm điều này:

type1 
.where(t1 => type2.Any(t2 => t1.common == t2.common) 
.ToDictionary(t1 => t1.Id) 

Hoặc như Servy gợi ý

type1 
    .Join(type2, a => a.common, b => b.common, (a1,b1) => a1) 
    .ToDictionary(t1 => t1.Id) 
+0

Điều này khá hiệu quả hơn một chút so với 'Tham gia'. Bạn đang thực hiện rất nhiều lần lặp của 'type2' ở đây thông qua các tìm kiếm tuyến tính trái với việc thực hiện các hoạt động dựa trên tập hợp. Bạn cũng không parinig mục 't1' với bạn tình trong' type2'. – Servy

+0

@Servy bạn là chính xác. Hiệu quả không bao giờ là một mối quan tâm. Tôi đã cố gắng truyền tải các khái niệm LINQ cho một người quen thuộc hơn với các vòng lặp. Tại sao bạn không đăng câu trả lời của mình! –

+0

Tại sao tôi đăng câu trả lời khi đã có hai câu trả lời giải quyết vấn đề này * đúng cách *, đặc biệt khi có từ Eric Lippert. Chuyển tải các khái niệm LINQ, trong tâm trí của tôi, có nghĩa là giảng dạy các phương pháp thích hợp (s) cho các nhiệm vụ ở bàn tay, mà điều này là không. Như một quy tắc chung, bất kỳ lúc nào bạn thấy mẫu bạn đang sử dụng 'Where' với một' Bất kỳ 'lồng nhau, có gì đó không ổn. 'type2' phải là một' HashSet' sử dụng 'Contains' nếu có thể. Trong mọi trường hợp, giải pháp này * thậm chí không hoạt động *. Kết quả cần phải là cặp * của các mục, không chỉ là một trong số chúng. – Servy

Các vấn đề liên quan