2012-03-22 75 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); 
相關問題