Сравните и объедините два списка‹T› в новый список‹T›

Я пытаюсь выяснить, как лучше всего сравнить и объединить два List<T> с новым генерируемым List<T>, который сравнивает несколько свойств в каждом объекте.

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);

Полученный List<Account> Ours будет иметь следующие List<Account> объекты:

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

Мне нужно выяснить, как лучше всего сравнивать свойства ID и Value между обоими объектами List<Account>, где List<Account> Yours ID имеет приоритет над List<Account> Mine, а List<Account> Mine Value имеет приоритет над List<Account> Yours, а также добавляется любой объект, которого нет в List<Account> Yours.

Я пробовал следующее:

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

что приводит к тому, что List<Ours> становится пустым.

Я прочитал этот пост Разница между двумя списками, в котором Джон Скит упоминает использование пользовательского IEqualityComparer<T> для делаю то, что мне нужно, но я застрял в том, как создать IEqualityComparer<T>, который сравнивает более 1 значения свойства.


person devin M. arnold    schedule 31.10.2013    source источник
comment
Как вы определяете, что два элемента представляют одно и то же, но с разными данными? просто по имени и значению?   -  person Ivo    schedule 31.10.2013
comment
По свойству Name, поэтому, если свойства имени объектов одинаковы, но значение отличается от одного объекта к другому, список‹Account› Mine будет иметь приоритет, но ID List‹Account› Yours переопределит нулевое значение для ID в Списке‹Аккаунт› Мой. Надеюсь, это имеет смысл.   -  person devin M. arnold    schedule 31.10.2013


Ответы (3)


Не уверен, что это можно сделать в "pue" LINQ, но немного процедурного кода поможет:

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);
}

После этого печать Ours...

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

... дает следующий результат:

1       C_First joe
2       C_Last  bloggs
        C_Car   ford
person Branko Dimitrijevic    schedule 31.10.2013
comment
Есть ли способ предотвратить добавление объектов с одинаковым именем и значением?Account m4 = new Account(){ ID = null, Name = C_Hair, Value = long }; Учетная запись y3 = новая учетная запись(){ ID = 1, Name = C_Hair, Value = long }; - person devin M. arnold; 31.10.2013
comment
@devinM.arnold Если я правильно понял, чего вы хотите достичь, вам нужно создать класс ключей, который объединяет Name и Value, а затем использовать его в качестве ключа словаря (вместо строки) . Просто будьте осторожны, чтобы правильно реализовать IEquatable. - переопределить GetHashCode. - person Branko Dimitrijevic; 31.10.2013
comment
добавление скобок вокруг оператора if, а затем добавление этого условного выражения помогло (y.Value != m.Value) - person devin M. arnold; 31.10.2013
comment
это имеет смысл в создании отдельного класса ключей, спасибо, что указали мне правильное направление. ваше здоровье! - person devin M. arnold; 31.10.2013

Попробуй это:

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();
person Ivo    schedule 31.10.2013
comment
Intellisense говорит, что последний x в (x => x.Name, x) не существует в текущем контексте. - person devin M. arnold; 31.10.2013
comment
Исправлено. Кроме того, код предназначен только для того, чтобы дать представление о возможном решении, вам нужно приложить немного усилий. - person Ivo; 31.10.2013
comment
достаточно Mine.ToDictionary(x=>x.Name) - person Grundy; 31.10.2013
comment
Продолжал получать ошибку исключения в отношении дубликатов ключей ivowiblo - person devin M. arnold; 31.10.2013
comment
Гранди, это сработало, но все значения идентификатора в результирующем словаре были нулевыми. - person devin M. arnold; 31.10.2013
comment
Вот почему вам нужно выполнить foreach в списке Yours, чтобы получить идентификаторы для тех значений, которые имеют нулевой идентификатор. - person Ivo; 12.11.2013

попробуйте этот код для 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();
    }
}

используется так: Ours = Mine.Except(Yours, new D()).ToList();

person Grundy    schedule 31.10.2013
comment
Используется ли он так: Mine.ToDictionary(x => x.Name, new D()); - person devin M. arnold; 31.10.2013