100r
100r

Reputation: 1104

Join two list comparing their elements properties

public class Person()
    {
    int ID;
    string Name;
    DateTime ChangeDate
    } 



var list1 = new List<Person>
    {
        new Person { ID= 1, Name = "Peter", ChangeDate= "2011-10-21" },
        new Person { ID= 2, Name = "John",  ChangeDate= "2011-10-22" },
        new Person { ID= 3, Name = "Mike",  ChangeDate= "2011-10-23" },
        new Person { ID= 4, Name = "Dave",  ChangeDate= "2011-10-24" }
    };

 var list2 = new List<Person>
    {
        new Person { ID= 1, Name = "Pete",  ChangeDate= "2011-10-21" },
        new Person { ID= 2, Name = "Johny", ChangeDate= "2011-10-20" },
        new Person { ID= 3, Name = "Mikey", ChangeDate= "2011-10-24" },
        new Person { ID= 5, Name = "Larry", ChangeDate= "2011-10-27" }
    };

As output I would like to have list1 + list2 =

    Person { ID= 1, Name = "Peter", ChangeDate= "2011-10-21" },
    Person { ID= 2, Name = "John",  ChangeDate= "2011-10-22" },
    Person { ID= 3, Name = "Mikey", ChangeDate= "2011-10-24" },
    Person { ID= 4, Name = "Dave",  ChangeDate= "2011-10-24" }
    Person { ID= 5, Name = "Larry", ChangeDate= "2011-10-27" }

And the Algorithm is like this. Join two list. If elements of lists have same ID, compare them by ChangeDate and take the ond with bigger date. If ChangeDate are equeal take any of them but not both. Maybe its easier to concat both lists and than to filter them with lambda. I tried, but always came out with some ugly code :/

Anyone have any idea?

Upvotes: 2

Views: 1689

Answers (4)

Merlyn Morgan-Graham
Merlyn Morgan-Graham

Reputation: 59151

You could concatenate and sort them, then get distinct values:

class PersonIdEqualityComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.ID == y.ID;
    }

    public int GetHashCode(Person person)
    {
        return person.ID;
    }
}

var result = list1.Concat(list2)
    .OrderByDescending(i => DateTime.Parse(i.ChangeDate)) // Most recent first
    .Distinct(new PersonIdEqualityComparer())
    ;

This assumes that Distinct will take the first item encountered out of each set rather than an arbitrary item. Considering it probably just inserts them into a HashSet while traversing the collection, this seems reasonable to me.

It also does no error checking. If any values could be null, or ChangeDate could be invalid, then this code will throw exceptions. If this could be a problem I suggest you check your data before passing it in, and that you do error checking in the PersonIdEqualityComparer class.

Upvotes: 0

Francis
Francis

Reputation: 3383

LINQ

var q = from person in list1.Concat(list2)
        group person by person.ID into g
        select g.OrderByDescending(p => p.ChangeDate).First();

Upvotes: 4

Stuart Golodetz
Stuart Golodetz

Reputation: 20656

How about something like this?

using System;
using System.Collections.Generic;
using System.Linq;

public class Person
{
    public int ID;
    public string Name;
    public DateTime ChangeDate;
}

public class PersonComparer : IEqualityComparer<Person>
{
    public bool Equals(Person p1, Person p2)
    {
        return p1.ID == p2.ID;
    }

    public int GetHashCode(Person p)
    {
        return p.ID.GetHashCode();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var list1 = new List<Person>
        {
            new Person { ID = 1, Name = "Peter", ChangeDate = DateTime.Parse("2011-10-21") },
            new Person { ID = 2, Name = "John",  ChangeDate = DateTime.Parse("2011-10-22") },
            new Person { ID = 3, Name = "Mike",  ChangeDate = DateTime.Parse("2011-10-23") },
            new Person { ID = 4, Name = "Dave",  ChangeDate = DateTime.Parse("2011-10-24") }
        };

        var list2 = new List<Person>
        {
            new Person { ID = 1, Name = "Pete",  ChangeDate = DateTime.Parse("2011-10-21") },
            new Person { ID = 2, Name = "Johny", ChangeDate = DateTime.Parse("2011-10-20") },
            new Person { ID = 3, Name = "Mikey", ChangeDate = DateTime.Parse("2011-10-24") },
            new Person { ID = 5, Name = "Larry", ChangeDate = DateTime.Parse("2011-10-27") }
        };

        var pc = new PersonComparer();
        var combined = list1.Join(list2, p => p.ID, p => p.ID, (p1,p2) => p2.ChangeDate > p1.ChangeDate ? p2 : p1)
                            .Union(list1.Except(list2, pc))
                            .Union(list2.Except(list1, pc));

        foreach(var p in combined)
        {
            Console.WriteLine(p.ID + " " + p.Name + " " + p.ChangeDate);
        }
    }
}

Upvotes: 0

kingsindian
kingsindian

Reputation: 183

Merge both lists sorting on descending date. Now you need to take the first occurence of each id in your sorted list.

Upvotes: 0

Related Questions