3

I've got the following class

public class Application
{
    public int Id { get; set; }

    public int Version { get; set; }

    (...)
}

And I have the following IEnumerable<Application>:

IEnumerable<Application> applications1 = new List<Application>
{
    new Application {Id = 1, Version = 1},
    new Application {Id = 2, Version = 1},
    new Application {Id = 3, Version = 3}
};

IEnumerable<Application> applications2 = new List<Application>
{
    new Application {Id = 1, Version = 2},
    new Application {Id = 3, Version = 2}
    new Application {Id = 4, Version = 1}
};

How can I merge them into a single IEnumerable<Application> using LINQ while ensuring that if two Applications have the same Id only the one with the highest Version is added to the new IEnumerable<Application>, effectively my final `IEnumerable should be equivalent to:

IEnumerable<Application> final = new List<Application>
{
    new Application {Id = 1, Version = 2},
    new Application {Id = 2, Version = 1},
    new Application {Id = 3, Version = 3},
    new Application {Id = 4, Version = 1}
}
cogumel0
  • 2,430
  • 5
  • 29
  • 45
  • 3
    @PavelAnikhouski That won´t do anything on the `Version`. Thus it´s completely random which of the two versions will survive. – MakePeaceGreatAgain Jun 04 '20 at 13:19
  • Does this answer your question? [Combining 2 lists and and remove duplicates .Output in a third list .My attempts do not work](https://stackoverflow.com/questions/3682437/combining-2-lists-and-and-remove-duplicates-output-in-a-third-list-my-attempts) – Pavel Anikhouski Jun 04 '20 at 13:20
  • @PavelAnikhouski that doesn't really answer my question because his objects are _actually_ equal, mine aren't, but thx for bringing it up. – cogumel0 Jun 04 '20 at 13:23
  • Does this answer your question? [Merging two IEnumerables](https://stackoverflow.com/questions/590991/merging-two-ienumerablets) – Heretic Monkey Jun 04 '20 at 14:24

3 Answers3

5

You can use GroupBy combined with selecting maximum in group via Aggregate:

IEnumerable<Application> final = applications1
    .Concat(applications2)
    .GroupBy(a => a.Id)
    .Select(g => g.Aggregate((acc, curr) => acc.Version > curr.Version ? acc: curr))
    .ToList();
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
1

A more procedural way would be:

var concat = applications1.Concat(applications2)
    .ToArray();

var final = concat
    .GroupBy(a => a.Id)
    .Select (g => new
    {
        ApplicationId = g.Key,
        MaxVersion = g.Max(i => i.Version)
    })
    .Select(i => concat.FirstOrDefault(
        existing => existing.Id == i.ApplicationId && 
                    existing.Version == i.MaxVersion))
    .ToList();
Rubens Farias
  • 57,174
  • 8
  • 131
  • 162
1

Why just don't write something like that?

var result = applications1
    .Concat(applications2).GroupBy(x => x.Id)
    .Select(g => new Application { Id = g.Key, Version = g.Max(a => a.Version) });

You concatenate both collections, group items by Id, then select new Application instance with group key as an Id and max Version.

If you don't want to create a new Application instance, the following code might help, by looking a max Version for every group and return an Application with max Version value

var result = applications1
    .Concat(applications2).GroupBy(x => x.Id)
    .Select(g =>
    {
        var maxVersion = g.Max(a => a.Version);
        return g.First(a => a.Version == maxVersion);
    });
Pavel Anikhouski
  • 21,776
  • 12
  • 51
  • 66
  • I know this would work in the example given, but it assumes that the class only has two properties and both have a setter, so that a new object may be constructed. This is unfortunately not quite the case here. I'm still upvoting because it does work in the example given given – cogumel0 Jun 04 '20 at 14:15
  • @cogumel0 Yes, I assuming your example. If there are more properties here, you should consider how to manage them - add to grouping, use the first/last/average/max and so on – Pavel Anikhouski Jun 04 '20 at 14:18
  • I guess what I'm trying to say is that I think there are better ways that don't involve recreating the objects. – cogumel0 Jun 04 '20 at 14:22
  • @cogumel0 Got your point, I've added another code snippet, which doesn't require recreating of objects – Pavel Anikhouski Jun 04 '20 at 15:07