2012-03-22 65 views
2

我得到了两个对象集合。使用LINQ合并集合,同时覆盖具有相同ID的行

例如:

List<Foo> firstFoos = new List<Foo>(); 
List<Foo> secondFoos = new List<Foo>(); 

firstFoos.Add(new Foo() { Id = 1, ValueA = 10, ValueB = 15 }); 
firstFoos.Add(new Foo() { Id = 2, ValueA = 20, ValueB = 25 }); 
firstFoos.Add(new Foo() { Id = 3, ValueA = 30, ValueB = 35 }); 
firstFoos.Add(new Foo() { Id = 4, ValueA = 40, ValueB = 45 }); 

secondFoos.Add(new Foo() { Id = 1, ValueA = 100, ValueB = 150 }); 
secondFoos.Add(new Foo() { Id = 2, ValueA = 200, ValueB = 250 }); 

使用LINQ,我怎么能合并这两个集合覆盖由secondFoos具有相同ID firstFoos

预期的结果是:

| Id | ValueA | ValueB | 
|---------|--------|--------| 
| 1 | 100 | 150 | 
| 2 | 200 | 250 | 
| 3 | 30 | 35 | 
| 4 | 40 | 45 | 

请注意,这个例子情况下只有两个值的列(ValueAValueB),但实际情况可能有更多。

回答

4

我想将其转换为Id -> Foo字典,然后只需用普通的foreach更新:

var fooDict = firstFoos.ToDictionary(foo => foo.Id, foo => foo); 
foreach (var foo in secondFoos) 
    fooDict[foo.Id] = foo; 
var newFoos = fooDict.Values.OrderBy(foo => foo.Id).ToList(); 
3

您可以自定义一个相等比较,并使用Union()

public class FooComparer : IEqualityComparer<Foo> 
{ 
    public bool Equals(Foo x, Foo y) 
    { 
     return x.Id == y.Id; 
    } 

    public int GetHashCode(Foo obj) 
    { 
     return obj.Id.GetHashCode(); 
    } 
} 

然后:

var mergedList = secondFoos.Union(firstFoos, new FooComparer()) 
          .ToList(); 

这使用事实在firstFoo中的任何项目之前将添加到所得到的枚举中,因此具有已经存在的Id的firstFoo中的任何项目将被过滤掉。这当然假定Id应该在所有项目中不同。

+0

这是保证从secondFoos返回元素时,都存在?另外,你的GetHashCode方法需要返回obj.Id(或者只与id相关的哈希)。 – Random832 2012-03-22 19:22:39

+0

@ Random832:这就是当前对象的Linq是如何工作的 - 正如文档中指定的那样**联合会按照该顺序枚举第一个和第二个,并产生每个尚未产生的元素**和是 - hashcode仅在“ Id'属性,因为这是唯一与此比较器相关的属性。 – BrokenGlass 2012-03-22 19:29:55

+0

我可以发誓,当我看到它时,它只有obj.GetHashCode。我看到了这个陈述,但是不能确定它暗示它不能从第二个集合[firstFoos]中得到第一个集合的order_中的对象。我认为这个问题对我来说是不足以确定“每个元素”意味着什么是非默认的等价关系。我不怀疑它目前的工作方式,规范保证它始终会保持不变。 – Random832 2012-03-23 05:39:03

1

这应该为你工作

var concat = firstFoos.Select(x => new { Foo = x, list=1 }) 
         .Concat(secondFoos.Select(x => new { Foo = x, list= 2 }); 

var merge = from x in concat 
      group x by x.Foo.Id into x 
      select x.Count() == 1 ? x.First().Foo : x.First(y => y.list == 2).Foo; 
2
var result = secondFoos.Concat(
    firstFoos.Except(secondFoos, 
    new LambdaComparer<Foo>((a, b) => a.Id == b.Id))) 
    .ToList(); 

另一种选择,因为你永远不会有同样的问题太多了解决方案;)

0

另一种选择

var f1NotInF2 = from f1 in firstFoos 
    where !secondFoos.Exists(f2 => f1.Id == f2.Id) 
    select f1; 
var mixed = f1NotInF2.Concat(secondFoos); 
+0

添加'OrderBy'将会是最完美的答案。 – code4life 2012-03-22 20:01:34

0

我会使用这样的东西:

  List<Foo> newFoos = new List<Foo>(); 
      Foo selected = null; 
      foreach (Foo foo in firstFoos) 
      { 
       selected = secondFoos.FirstOrDefault(x => x.Id == foo.Id); 
       if (selected != null) 
       {     
        newFoos.Add(selected);     
       } 
       else 
       { 

        newFoos.Add(foo); 
       } 
      } 
1

这将工作:

var merged = firstFoos.Where(f => !secondFoos.Any(s => s.Id == f.Id)) 
    .Union(secondFoos).OrderBy(c=>c.Id); 
相关问题