2011-10-11 15 views
7

tengo dos listas, una falsa y uno real, como:¿Cómo combinar dos listas basadas en una propiedad?

ANTES

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

quiero fusionarlas en busca del Año, el resultado debería ser:

DESPUÉS

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

Debe quitar los elementos con el mismo año en la primera lista y agregue el elemento con el año equivalente en la segunda lista a la primera lista, manteniendo el orden.

¿Cómo puedo hacerlo usando Linq?

+0

¿Puede aclarar quizás usando una ilustración de antes/después? me has perdido por completo ahora – sehe

Respuesta

9

Usted debe ser capaz de hacer que el uso de una "combinación izquierda": consulta

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

Esa última línea debe devolver 'f.X' cuando la condición es verdadera, en lugar de asumir que siempre será la cadena vacía, pero de lo contrario, +1 –

+0

@djacobson - Ya está arreglado. –

+0

Danos actualizaciones de estilo AJAX en las ediciones de respuestas, ASÍ, en serio. ;) –

5

de Justin es la manera más eficiente de hacerlo, pero si usted está preocupado con el mantenimiento de objetos idénticos (y no crear nuevos registros de la consulta) que podría hacer de esta manera:

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

en lugar de definir la lista falso sí mismo, trate de hacer que LINQ lo haga por usted:

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

Esto producirá exactamente el conjunto de resultados que desee. Sin embargo, es probable que necesite definir un tipo con nombre para los elementos de la lista, ya que dos tipos anónimos definidos por separado no pueden convertirse implícitamente aunque tengan todos los mismos tipos/nombres de miembros.

1

Usando IEnumerable.Union y IEqualityComparer.

P.S. Esto daría como resultado un resultado diferente en comparación con la combinación izquierda si la lista real tuviera más elementos (años que no están presentes en la lista falsa). La combinación izquierda no devolvería los resultados que podrían ser un resultado deseado (no está claro desde 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()); 
} 
Cuestiones relacionadas