Question

I'm trying to figure out how best to compare and merge two List<T> with a new List<T> being generated that compares multiple properties within each object.

class Account
{
    public Account() { }
    public string ID { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
}

List<Account> Mine = new List<Account>();
List<Account> Yours = new List<Account>();
List<Account> Ours = new List<Account>();

Account m1 = new Account(){ ID = null, Name = "C_First", Value = "joe" };
Account m2 = new Account(){ ID = null, Name = "C_Last", Value = "bloggs" };
Account m3 = new Account(){ ID = null, Name = "C_Car", Value = "ford" };

Mine.Add(m1);
Mine.Add(m2);
Mine.Add(m3);

Account y1 = new Account(){ ID = "1", Name = "C_First", Value = "john" };
Account y2 = new Account(){ ID = "2", Name = "C_Last", Value = "public" };

Yours.Add(y1);
Yours.Add(y2);

The resulting List<Account> Ours would have the following List<Account> objects:

{ ID = "1", Name = "C_First", Value = "joe" };
{ ID = "2", Name = "C_Last", Value = "bloggs" };
{ ID = null, Name = "C_Car", Value = "ford" };

I need to figure out how best to compare the ID and Value properties between both List<Account> objects where the List<Account> Yours ID takes precedence over the List<Account> Mine and the List<Account> Mine Value takes precedence over List<Account> Yours along with any object that's not in List<Account> Yours being added as well.

I've tried the following:

Ours = Mine.Except(Yours).ToList();

which results in List<Ours> being empty.

I've read this post Difference between two lists in which Jon Skeet mentions using a custom IEqualityComparer<T> to do what I need but I'm stuck on how to create an IEqualityComparer<T> that compares more than 1 property value.

Was it helpful?

Solution

Not sure if it can be done in "pue" LINQ, but a bit of procedural code would do the trick:

var dict = Yours.ToDictionary(y => y.Name);
foreach (var m in Mine) {
    Account y;
    if (dict.TryGetValue(m.Name, out y))
        Ours.Add(new Account { ID = y.ID, Name = m.Name, Value = m.Value });
    else
        Ours.Add(m);
}

After that, printing Ours...

foreach (var o in Ours)
    Console.WriteLine("{0}\t{1}\t{2}", o.ID, o.Name, o.Value);

...gives the following result:

1       C_First joe
2       C_Last  bloggs
        C_Car   ford

OTHER TIPS

Try this:

var index = Mine.ToDictionary(x => x.Name);

foreach(var account in Yours)
{
    if(index.ContainsKey(account.Name))
    {
        var item = index[account.Name]; 
        if(item.ID == null)
            item.ID = account.ID;
    }
    index.Add(account.Name, account);
}

Ours = index.Values.ToList();

try this code for IEqualityComparer:

public class D : IEqualityComparer<Account>
{
    public bool Equals(Account x, Account y)
    {
        return x.ID == y.ID && x.Value==y.Value;
    }

    public int GetHashCode(Account obj)
    {
        return obj.ID.GetHashCode() ^ obj.Value.GetHashCode();
    }
}

used like this: Ours = Mine.Except(Yours, new D()).ToList();

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top