2011-10-11 5 views
7

У меня есть два списка, один поддельных и один реальные, как:Как объединить два списка на основе свойства?

ПЕРЕД

// 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" } 

Я хочу, чтобы объединить их выглядящими за год, результат должен быть:

ПОСЛЕ

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

Необходимо удалить элементы с тем же годом в первом списке и добавьте элемент с эквивалентным Годом во второй список в первый список, сохранив заказ.

Как я могу использовать Linq?

+0

Возможно, вы можете уточнить использование иллюстрации до и после? теперь вы полностью потеряли – sehe

ответ

9

Вы должны быть в состоянии сделать это с помощью «левого соединения»: запрос

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

. Последняя строка должна возвращать 'f.X', когда условие истинно, вместо того, чтобы предполагать, что оно всегда будет пустой строкой, но в остальном, +1 –

+0

@djacobson - уже исправлено. –

+0

Дайте нам AJAX-стиль освежает ответы на ответ, так, серьезно. ;) –

5

Джастина является наиболее эффективным способом сделать это, но если вы обеспокоены с сохранением идентичных объектов (а не создавать новые записи из запроса), вы могли бы сделать это следующим образом:

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

Вместо определения поддельного списка самостоятельно, попытайтесь иметь Linq сделать это для вас:

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); 

Это приведет к точному набору результатов, который вы хотите. Скорее всего, вам нужно будет определить именованный тип для элементов списка, поскольку два отдельно определенных анонимных типа не могут быть неявно преобразованы, даже если они имеют одинаковые типы/имена членов.

1

Использование IEnumerable.Union и IEqualityComparer.

P.S. Это приведет к другому результату по сравнению с левым объединением, если в реальном списке было больше элементов (лет, отсутствующих в поддельном списке). Левое соединение не будет возвращать те результаты, которые могут быть желаемым результатом (непонятно из 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()); 
} 
Смежные вопросы