2011-10-11 47 views
7

Tôi có hai danh sách, một giả và một thực tế, như:Làm cách nào để hợp nhất hai danh sách dựa trên một thuộc tính?

TRƯỚC

// fake (list 1) 
{ ID = 1, Year = 2011, X = "" } 
, { ID = 2, Year = 2012, X = "" } 
, { ID = 3, Year = 2013, X = "" } 

// real (list 2) 
{ ID = 35, Year = 2011, X = "Information" } 
, { ID = 77, Year = 2013, X = "Important" } 

Tôi muốn kết hợp chúng tìm kiếm các năm, kết quả nên là:

SAU

{ ID = 35, Year = 2011, X = "Information" } 
, { ID = 2, Year = 2012, X = "" } 
, { ID = 77, Year = 2013, X = "Important" } 

Phải xóa phần tử với cùng năm trong danh sách đầu tiên và thêm phần tử có Năm tương đương vào danh sách thứ hai vào danh sách đầu tiên, giữ nguyên thứ tự.

Tôi làm cách nào để sử dụng LINQ?

+0

có lẽ bạn có thể làm rõ bằng cách sử dụng hình minh họa trước/sau? bạn đã hoàn toàn bị mất ngay bây giờ – sehe

Trả lời

9

Bạn sẽ có thể làm điều đó bằng cách sử dụng "trái tham gia": truy vấn

from f in fake 
join r in real 
on f.Year equals r.Year 
into joinResult 
from r in joinResult.DefaultIfEmpty() 
select new 
     { 
      ID = r == null ? f.ID : r.ID, 
      Year = f.Year, 
      X = r == null ? f.X : r.X 
     }; 
+0

Dòng cuối cùng sẽ trả về 'f.X' khi điều kiện là đúng, thay vì giả định rằng nó sẽ luôn là chuỗi trống, nhưng nếu không, +1 –

+0

@djacobson - Đã được sửa. –

+0

Hãy cho chúng tôi làm mới phong cách AJAX về chỉnh sửa câu trả lời, SO, nghiêm túc. ;) –

5

Justin là cách hiệu quả nhất để làm điều đó, nhưng nếu bạn đang quan tâm đến việc giữ các đối tượng giống hệt nhau (và không tạo kỷ lục mới từ truy vấn), bạn có thể làm điều đó như thế này:

var combined = from f in fake 
       let r = (from r1 in real 
         where r1.Year == f.Year 
         select r1).SingleOrDefault() 
       select r ?? f; 
1

Thay vì xác định danh sách giả mạo chính mình, cố gắng có LINQ làm điều đó cho bạn:

Enumerable.Range(2011,3) //2011, 2012, 2013    
      //use the overload that provides a 0-based ordinal position of each element 
      .Select(x,i=> new {ID = i+1, Year = x, X = String.Empty) 
      //now you have your fake list; join with the "real" list based on Year fields, 
      //taking the real element wherever it exists and the fake one otherwise 
      .Join(real, l=>l.Year, r=>r.Year, (l,r) => r == null ? l : r); 

Điều này sẽ tạo ra chính xác tập hợp kết quả bạn muốn. Bạn có thể sẽ cần định nghĩa một kiểu được đặt tên cho các mục danh sách, vì hai loại vô danh được định nghĩa riêng biệt không thể được chuyển đổi hoàn toàn ngay cả khi chúng có tất cả các kiểu/tên thành viên giống nhau.

1

Sử dụng IEnumerable.UnionIEqualityComparer.

P.S. Điều này sẽ dẫn đến kết quả khác khi so sánh với phép nối trái nếu danh sách thực có nhiều phần tử hơn (năm không có trong danh sách giả). Việc kết nối trái sẽ không trả lại các kết quả đó có thể là kết quả mong muốn (không rõ ràng từ OP).

public class MyClass 
{ 
    public int ID {get; set;} 
    public int Year {get; set;} 
    public string X {get; set;} 
} 

public class MyClassEqualityComparer : IEqualityComparer<MyClass> 
{ 
    public bool Equals(MyClass x, MyClass y) 
    { 
     return x.Year == y.Year; 
    } 

    public int GetHashCode(MyClass obj) 
    { 
     return obj.ToString().ToLower().GetHashCode(); 
    } 
} 

void Main() 
{ 
    var fake = new List<MyClass> { 
      new MyClass { ID = 1, Year = 2011, X = "" } 
     , new MyClass { ID = 2, Year = 2012, X = "" } 
     , new MyClass { ID = 3, Year = 2013, X = "" } 
    }; 

    var real = new List<MyClass> { 
      new MyClass { ID = 35, Year = 2011, X = "Information" } 
     , new MyClass { ID = 77, Year = 2013, X = "Important" } 
    }; 

    var merged = real.Union(fake, new MyClassEqualityComparer()); 
} 
Các vấn đề liên quan