2011-10-11 22 views
7

Mam dwa listy, jeden jeden prawdziwy fałszywy i tak jak:Jak scalić dwie listy na podstawie właściwości?

PRZED

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

Chcę połączyć je patrząc na rok, wynik powinien być:

PO

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

To musi usunąć elementy z tym samym rokiem na pierwszej liście i dodaj element z równoważnym Rokiem na drugiej liście do pierwszej listy, zachowując zamówienie.

Jak mogę to zrobić, używając Linq?

+0

Być może możesz wyjaśnić, używając ilustracji przed/po? teraz mnie całkowicie straciłeś – sehe

Odpowiedz

9

Powinieneś być w stanie to zrobić za pomocą „left join”: zapytanie

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

Ten ostatni wiersz powinien zwrócić 'f.X' gdy warunek jest prawdziwy, zamiast zakładać, że zawsze będzie to pusty ciąg, ale poza tym, +1 –

+0

@djacobson - Już poprawiony. –

+0

Daj nam styl AJAX odświeżający na edycje odpowiedzi, SO, poważnie. ;) –

5

Justina jest najbardziej skutecznym sposobem, aby to zrobić, ale jeśli jesteś zaniepokojony z zachowaniem identycznych obiektów (a nie tworzenie nowe rekordy z zapytania) można zrobić to tak:

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

Zamiast definiować fałszywy wykaz siebie, spróbuj mający LINQ to zrobić dla Ciebie:

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

To da dokładnie taki zestaw wyników, jaki chcesz. Prawdopodobnie będziesz musiał zdefiniować nazwany typ dla elementów listy, ponieważ dwa oddzielnie zdefiniowane typy anonimowe nie mogą być niejawnie konwertowane, nawet jeśli mają wszystkie te same typy/nazwy członków.

1

Korzystanie z IEnumerable.Union i IEqualityComparer.

P.S. Spowoduje to inny wynik w porównaniu do lewostronnego, jeśli lista rzeczywista zawiera więcej elementów (lata, których nie ma na liście fałszywych). Lewe połączenie nie zwróci wyników, które mogą być pożądanym wynikiem (nie wynika to z PO).

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()); 
} 
Powiązane problemy