Удаление дубликатов при слиянии списков с помощью Union в LINQ
Я пытаюсь объединить два списка, используя list.Union
в LinqPad
, но я не могу заставить его работать и хочу проверить, что мое понимание верное.
Учитывая этот простой класс:
public class Test
{
public int Id { get; set;}
public int field1 { get; set; }
public bool Equals(Test other)
{
return this.Id.Equals(other.Id);
}
}
И два списка заполнены следующим образом:
List<Test> list = new List<Test>();
list.Add( new Test { Id = 1, field1 = 1});
list.Add( new Test { Id = 1, field1 = 2});
list.Add( new Test { Id = 2, field1 = 3});
list.Add( new Test { Id = 2, field1 = 4});
List<Test> list2 = new List<Test>();
list2.Add( new Test { Id = 1, field1 = 1});
list2.Add( new Test { Id = 1, field1 = 2});
list2.Add( new Test { Id = 2, field1 = 3});
list2.Add( new Test { Id = 2, field1 = 4});
Затем я попытаюсь: var mergedList = list.Union(list2).ToList();
и вывести данные с помощью простого цикла foreach
и получить этот вывод:
ID: 1 -------- 1
ID: 1 -------- 2
ID: 2 -------- 3
ID: 2 -------- 4
ID: 1 -------- 1
ID: 1 -------- 2
ID: 2 -------- 3
ID: 2 -------- 4
У меня создалось впечатление, что Union
должен удалить возвращаемые дубликаты:
ID: 1 -------- 1
ID: 1 -------- 2
ID: 2 -------- 3
ID: 2 -------- 4
Я что-то делаю неправильно или неправильно понял?
Кроме того, должно ли оно работать без явного переопределения метода Equals
в классе Test
?
Спасибо
Ответы
Ответ 1
В вашем случае вы просто определяете какой-то метод, о котором LINQ ничего не знает. Это похоже на метод создания bool HeyEquateMeWith(Test other)
и ожидать, что LINQ будет вызывать его при выполнении заданий.
Вам нужно определить свой класс следующим образом ( переопределить Object
Equals
и GetHashCode
):
public class Test
{
public int Id { get; set;}
public int field1 { get; set; }
public override bool Equals(object other) //note parameter is of type object
{
Test t = other as Test;
return (t != null) ? Id.Equals(t.Id) : false;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
Теперь Union
вызовет ваши переопределенные методы Equals
и GetHashCode
. Также вы должны ВСЕГДА отменить GetHashCode
при переопределении метода Equals
.
Ответ 2
Вы можете создать класс, реализующий
IEqualityComparer<Test>
Этот класс определяет Equals и GetHashCode
После этого вы можете передать этот компаньон вам методом Union
Точно так же:
public class MyComparer:IEqualityComparer<Test>{
//Equals and GetHashCode
}
var mergedList = list.Union(list2, new MyComparer()).ToList();
Ответ 3
Вы можете попробовать что-то подобное, если не довольны стандартным компаратором (который, в свою очередь, использует метод GetHashCode, как упоминал @IlyaIvanov):
// get all items that "other than in first list", so Where() and Any() are our filtering expressions
var delta = list2.Where(x2 => !list.Any(x1 => (x1.Id == x2.Id) && (x1.field1 == x2.field1)));
// now let merge two enumerables that have nothing "equal" between them
var merged = list.Union(delta).ToList();
Ответ 4
Просто хочу оставить это здесь для тех, кто еще не смог его получить. Я нашел эту статью очень полезной для класса сравнения, который наследует от IEqualityComparer
http://alicebobandmallory.com/articles/2012/10/18/merge-collections-without-duplicates-in-c