Reputation:
I got two collections of objects.
For example:
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 });
Using LINQ, how can I merge the two collection overriding firstFoos
by secondFoos
which have the same ID?
Expected result is:
| Id | ValueA | ValueB |
|---------|--------|--------|
| 1 | 100 | 150 |
| 2 | 200 | 250 |
| 3 | 30 | 35 |
| 4 | 40 | 45 |
Please note that this example case has only two value columns (ValueA
and ValueB
), but an actual case could have many more.
Upvotes: 3
Views: 2117
Reputation: 15794
This will work:
var merged = firstFoos.Where(f => !secondFoos.Any(s => s.Id == f.Id))
.Union(secondFoos).OrderBy(c=>c.Id);
Upvotes: 0
Reputation: 13391
I would use something like this:
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);
}
}
Upvotes: 0
Reputation: 39055
Another option
var f1NotInF2 = from f1 in firstFoos
where !secondFoos.Exists(f2 => f1.Id == f2.Id)
select f1;
var mixed = f1NotInF2.Concat(secondFoos);
Upvotes: 0
Reputation: 203825
var result = secondFoos.Concat(
firstFoos.Except(secondFoos,
new LambdaComparer<Foo>((a, b) => a.Id == b.Id)))
.ToList();
Another option, because you can never have too many solutions to the same problem ;)
Upvotes: 1
Reputation: 26694
This should work for you
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;
Upvotes: 1
Reputation: 160992
You can define a custom equality comparer and use 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();
}
}
And then:
var mergedList = secondFoos.Union(firstFoos, new FooComparer())
.ToList();
This uses the fact that items in secondFoos
are added to the resulting enumeration before any item in firstFoo
, any item in firstFoo
with an already existing Id will hence be filtered out. This assumes of course that Id
should be distinct across all items.
Upvotes: 3
Reputation: 47840
I'd convert it to an Id -> Foo
dictionary, and then just update with a regular 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();
Upvotes: 4