2011-06-09 59 views
6

我有自定义对象的两个列表:合并2只列出了LINQ

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

我想第三列表两者之间的合并:

List3: Year, Month, ValueA, ValueB 

是否有任何优雅的方式来在LINQ VB.Net中执行该操作?

实施例:

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 

预先感谢。

解决方案解决方案的 VB.Net翻译:

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) 

回答

8

当然,你希望对数据执行,这并不在LINQ存在的Full Outer Join,所以我们假它有两个被联合左外连接:

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

为C#很多道歉,我不能让我的VB.Net例如为工作我的生活。您需要两个左外部联接才能生成正确的答案。我尝试过的代码转换器都没有工作。


下面是VB.Net是LINQPad扼流圈,但每一个例子我能找到的说应该是正确的:

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

待机,这可能无法正常工作,因为它排除了在其他收集中不匹配的项目。 – user7116 2011-06-09 14:35:40

+0

感谢您的建议,但此解决方案没有考虑List2中的值,List1中没有对应值:在我的示例中,List2中的2010-8-1将会丢失。 – Joss57 2011-06-09 14:47:02

+0

@ Joss57:在您发表评论时,我看到了这个结论。我已更新我的示例以使用完全外部联接。 – user7116 2011-06-09 14:55:59

0

尝试用这种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); 
     } 

结果:

对象以更新
2010 -7 - 5 - 2
2010 -10 - 3 - 2
在源而不是在目标
2010 -6 - 2 - 0
在目标但不在源中
2010 -8 - 0 - 1