2011-06-09 12 views
6

tengo dos listas de objetos personalizados:La fusión de 2 Listas en LINQ

List1: Year, Month, ValueA 
List2: Year, Month, ValueB 

Quiero conseguir una tercera lista con una fusión entre los dos:

List3: Year, Month, ValueA, ValueB 

¿Hay alguna manera elegante de realizar eso en LINQ VB.Net?

Ejemplo:

List1: 
2010 - 6 - 2 
2010 - 7 - 5 
2010 - 10 - 3 

List2: 
2010 - 7 - 2 
2010 - 8 - 1 
2010 - 10 - 2 

List3 (result): 
2010 - 6 - 2 - 0 
2010 - 7 - 5 - 2 
2010 - 8 - 0 - 1 
2010 - 10 - 3 - 2 

Gracias de antemano.

Solución traducción VB.Net de la solución:

Dim ListA = From a In List1 
    Group Join b In List2 
    On a.Year Equals b.Year And a.Month Equals b.Month Into bGroup = Group 
    From b In bGroup.DefaultIfEmpty() 
    Select a.Year, a.Month, a.Value1, Value2 = If(b Is Nothing, 0, b.Value2) 
Dim ListB = From b In List2 
    Group Join a In List1 
    On b.Year Equals a.Year And b.Month Equals a.Month Into aGroup = Group 
    From a In aGroup.DefaultIfEmpty() 
    Select b.Year, b.Month, Value1 = If(a Is Nothing, 0, a.Value1), b.Value2 
Dim List3 = ListA.Union(ListB) 

Respuesta

8

Claro, usted está mirando para realizar una Full Outer Join en los datos, que no existen en LINQ, así que falsa con dos unioned externas por la izquierda:

var A = from a in List1 
    join b in List2 on new { a.Year, a.Month } equals new { b.Year, b.Month } 
     into bgroup 
    from b in bgroup.DefaultIfEmpty() 
    select new { a.Year, a.Month, a.ValueA, ValueB = (b == null ? 0 : b.ValueB) }; 

var B = from b in List2 
    join a in List1 on new { b.Year, b.Month } equals new { a.Year, a.Month } 
     into agroup 
    from a in agroup.DefaultIfEmpty() 
    select new { b.Year, b.Month, ValueA = (a == null ? 0 : a.ValueA), b.ValueB }; 

var List3 = A.Union(B); 

Muchas disculpas por la C#, no pude conseguir mi ejemplo VB.Net a trabajar para el vida de mi Necesita dos uniones externas izquierdas para unirse para producir la respuesta correcta. Ninguno de los convertidores de código que intenté funcionó tampoco.


El siguiente es VB.Net que se ahoga con LINQPad, pero todos los ejemplos que puedo encontrar dice debe ser correcta:

Dim A = From a In List1 _ 
    Group Join b In List2 _ 
     On New With { a.Year, a.Month } Equals New With { b.Year, b.Month} _ 
     Into bGroup = Group _ 
    From b In bGroup.DefaultIfEmpty() _ 
    Select a.Year, a.Month, a.ValueA, ValueB = If(b Is Nothing, 0, b.ValueB) 

Dim B = From b In List2 _ 
    Group Join a In List1 _ 
     On New With { b.Year, b.Month } Equals New With { a.Year, a.Month} _ 
     Into aGroup = Group _ 
    From a In aGroup.DefaultIfEmpty() _ 
    Select b.Year, b.Month, ValueA = If(a Is Nothing, 0, a.ValueA), b.ValueB 

Dim List3 = A.Union(B) 
+0

En espera, puede que esto no funcione, ya que excluye los elementos que no tienen una coincidencia en su otra colección. – user7116

+0

Gracias por su sugerencia, pero esta solución no tiene en cuenta los valores en List2 sin contraparte en List1: en mi ejemplo, 2010 - 8 - 1 en List2 se perderá. – Joss57

+0

@ Joss57: llegué a esa conclusión justo cuando publicabas ese comentario, parece. He actualizado mi muestra para usar una unión externa completa. – user7116

0

Pruebe con esta extensión LINQ

public interface IMerge<out T> 
{ 
    IEnumerable<IMergeMatched<T>> Matched(); 

    IEnumerable<IMergeMatched<T>> Matched(Func<T, T, bool> predicate); 

    IEnumerable<T> NotMatchedBySource(); 

    IEnumerable<T> NotMatchedBySource(Func<T, bool> predicate); 

    IEnumerable<T> NotMatchedByTarget(); 

    IEnumerable<T> NotMatchedByTarget(Func<T, bool> predicate); 
} 

public interface IMergeMatched<out T> 
{ 
    T Source { get; } 

    T Target { get; } 
} 

public static class Enumerable 
{ 
    public static IMerge<TSource> Merge<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> target, 
              Func<TSource, TSource, bool> predicate) 
    { 
     return new Merge<TSource>(source, target, predicate); 
    } 
} 

public class Merge<T> : IMerge<T> 
{ 
    private readonly Func<T, T, bool> _predicate; 
    private readonly IEnumerable<T> _source; 
    private readonly IEnumerable<T> _target; 
    private IEnumerable<IMergeMatched<T>> _matcheds; 
    private IEnumerable<T> _notMatchedBySource; 
    private IEnumerable<T> _notMatchedByTarget; 

    public Merge(IEnumerable<T> source, IEnumerable<T> taget, Func<T, T, bool> predicate) 
    { 
     _source = source; 
     _target = taget; 
     _predicate = predicate; 
    } 

    public IEnumerable<IMergeMatched<T>> Matched() 
    { 
     if (_matcheds == null) 
     { 
      Analize(); 
     } 
     return _matcheds; 
    } 

    public IEnumerable<IMergeMatched<T>> Matched(Func<T, T, bool> predicate) 
    { 
     return Matched() 
      .Where(t => predicate.Invoke(t.Source, t.Target)) 
      .ToArray(); 
    } 

    public IEnumerable<T> NotMatchedBySource() 
    { 
     if (_notMatchedBySource == null) 
     { 
      Analize(); 
     } 
     return _notMatchedBySource; 
    } 

    public IEnumerable<T> NotMatchedBySource(Func<T, bool> predicate) 
    { 
     return NotMatchedBySource() 
      .Where(predicate) 
      .ToArray(); 
    } 

    public IEnumerable<T> NotMatchedByTarget() 
    { 
     if (_notMatchedByTarget == null) 
     { 
      Analize(); 
     } 
     return _notMatchedByTarget; 
    } 

    public IEnumerable<T> NotMatchedByTarget(Func<T, bool> predicate) 
    { 
     return NotMatchedByTarget() 
      .Where(predicate) 
      .ToArray(); 
    } 

    private void Analize() 
    { 
     var macheds = new List<MergeMached<T>>(); 
     var notMachedBySource = new List<T>(_source); 
     var notMachedByTarget = new List<T>(_target); 

     foreach (var source in _source) 
     { 
      foreach (var target in _target) 
      { 
       var macth = _predicate.Invoke(source, target); 
       if (!macth) continue; 

       macheds.Add(new MergeMached<T>(source, target)); 
       notMachedBySource.Remove(source); 
       notMachedByTarget.Remove(target); 
      } 
     } 

     _matcheds = macheds.ToArray(); 
     _notMatchedBySource = notMachedBySource.ToArray(); 
     _notMatchedByTarget = notMachedByTarget.ToArray(); 
    } 
} 

public class MergeMached<T> : IMergeMatched<T> 
{ 
    public MergeMached(T source, T target) 
    { 
     Source = source; 
     Target = target; 
    } 

    public T Source { get; private set; } 

    public T Target { get; private set; } 
} 

Impementation :

var source = new List<CustomObject> 
      { 
       new CustomObject 
        { 
         Year = 2010, 
         Month = 6, 
         Value = 2 
        }, 
       new CustomObject 
        { 
         Year = 2010, 
         Month = 7, 
         Value = 5 
        }, 
       new CustomObject 
        { 
         Year = 2010, 
         Month = 10, 
         Value = 3 
        } 
      }; 

     var target = new List<CustomObject> 
      { 
       new CustomObject 
        { 
         Year = 2010, 
         Month = 7, 
         Value = 2 
        }, 
       new CustomObject 
        { 
         Year = 2010, 
         Month = 8, 
         Value = 1 
        }, 
       new CustomObject 
        { 
         Year = 2010, 
         Month = 10, 
         Value = 2 
        } 
      }; 

     var merge = source.Merge(target, (x, y) => x.Year == y.Year && x.Month == y.Month); 

     var toUpdate = merge.Matched((x, y) => x.Value != y.Value) 
      .ToArray(); 

     var inSourceButNotInTarget = merge.NotMatchedBySource(); 

     var inTargetButNotInSource = merge.NotMatchedByTarget(); 

     Console.WriteLine("Objects to Update"); 
     foreach (var mergeMatched in toUpdate) 
     { 
      Console.WriteLine("Source[{0} -{1} - {2} - {3}]", 
       mergeMatched.Source.Year, 
       mergeMatched.Source.Month, 
       mergeMatched.Source.Value, 
       mergeMatched.Target.Value); 
     } 

     Console.WriteLine("In source but not in target"); 
     foreach (var customObject in inSourceButNotInTarget) 
     { 
      Console.WriteLine("Source[{0} -{1} - {2} - 0]", 
           customObject.Year, 
           customObject.Month, 
           customObject.Value); 
     } 

     Console.WriteLine("In target but not in source"); 
     foreach (var customObject in inTargetButNotInSource) 
     { 
      Console.WriteLine("Source[{0} -{1} - 0 - {2}]", 
           customObject.Year, 
           customObject.Month, 
           customObject.Value); 
     } 

Resultado:

objetos para Actualización
2010 -7 - 5 - 2
2010 -10 - 3 - 2
En la fuente, pero no en destino
2010 -6 - 2 - 0. En
objetivo pero no en origen
2010 -8 - 0 - 1

Cuestiones relacionadas