Использование IEqualityComparer для объединения

Я просто хочу удалить дубликаты из двух списков и объединить их в один список. Мне также нужно иметь возможность определить, что такое дубликат. Я определяю дубликат по свойству ColumnIndex, если они одинаковые, они дубликаты. Вот мой подход:

Я нашел отличный пример того, как писать встроенные компараторы для случайных случаев, когда они нужны вам только один раз в сегменте кода.

public class InlineComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> getEquals;
    private readonly Func<T, int> getHashCode;

    public InlineComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        getEquals = equals;
        getHashCode = hashCode;
    }

    public bool Equals(T x, T y)
    {
        return getEquals(x, y);
    }

    public int GetHashCode(T obj)
    {
        return getHashCode(obj);
    }
}

Тогда у меня просто есть два списка, и я пытаюсь объединить их с помощью компаратора.

            var formatIssues = issues.Where(i => i.IsFormatError == true);
            var groupIssues = issues.Where(i => i.IsGroupError == true);

            var dupComparer = new InlineComparer<Issue>((i1, i2) => i1.ColumnInfo.ColumnIndex == i2.ColumnInfo.ColumnIndex, 
            i => i.ColumnInfo.ColumnIndex);

            var filteredIssues = groupIssues.Union(formatIssues, dupComparer);

Однако набор результатов равен нулю.

Где я сбиваюсь с пути? Я уже подтвердил, что в этих двух списках есть столбцы с одинаковыми свойствами ColumnIndex.


person Matthew Cox    schedule 11.05.2011    source источник
comment
Просто чтобы получить представление об этой проблеме, пробовали ли вы отлаживать код и уверены, что вызывается метод public bool Equals(T x, T y), а не метод public int GetHashCode(T obj)?   -  person avanek    schedule 11.05.2011
comment
Результат действительно null, а не пустая последовательность? Это было бы очень странно, так как Enumerable.Union() никогда не должно возвращать null.   -  person svick    schedule 11.05.2011


Ответы (3)


Я только что запустил ваш код на тестовом наборе... и он работает!

    public class InlineComparer<T> : IEqualityComparer<T>
    {
        private readonly Func<T, T, bool> getEquals;
        private readonly Func<T, int> getHashCode;

        public InlineComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
        {
            getEquals = equals;
            getHashCode = hashCode;
        }

        public bool Equals(T x, T y)
        {
            return getEquals(x, y);
        }

        public int GetHashCode(T obj)
        {
            return getHashCode(obj);
        }
    }

    class TestClass
    {
        public string S { get; set; }
    }

    [TestMethod]
    public void testThis()
    {
        var l1 = new List<TestClass>()
                     {
                         new TestClass() {S = "one"},
                         new TestClass() {S = "two"},
                     };
        var l2 = new List<TestClass>()
                     {
                         new TestClass() {S = "three"},
                         new TestClass() {S = "two"},
                     };

        var dupComparer = new InlineComparer<TestClass>((i1, i2) => i1.S == i2.S, i => i.S.GetHashCode());

        var unionList = l1.Union(l2, dupComparer);

        Assert.AreEqual(3, unionList);
    }

Итак... может быть, вернуться и проверить свои тестовые данные - или запустить его с другими тестовыми данными?

В конце концов, если Union пуст, это означает, что оба ваших списка ввода также пусты?

person Stuart    schedule 11.05.2011
comment
Ты прав. Я дважды проверил индексы столбцов, и ни один из них не равен. Момент Брайана пердеть. Вместо этого мне нужно было проводить сравнения на основе столбца идентификаторов. Это помогло мне обойти мой идиотский блок, поэтому я получил ответ = P - person Matthew Cox; 12.05.2011

Чуть более простой способ:

  • он сохраняет первоначальный порядок
  • он игнорирует дубликаты, когда находит их

Использует метод расширения ссылки:

   formatIssues.Union(groupIssues).DistinctBy(x => x.ColumnIndex)

Это DistinctBy лямбда-метод из MoreLinq

public static IEnumerable<TSource> DistinctBy<TSource, TKey>
     (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    HashSet<TKey> knownKeys = new HashSet<TKey>();
    foreach (TSource element in source)
    {
        if (knownKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}
person Simon_Weaver    schedule 07.06.2015
comment
Хорошее решение. Это проще, чем другие подходы. - person Nir Lanka; 22.01.2019

Разве метод Linq Except не сделает это за вас?

var formatIssues = issues.Where(i => i.IsFormatError == true);
var groupIssues = issues.Where(i => i.IsGroupError == true);

var dupeIssues = issues.Where(i => issues.Except(new List<Issue> {i})
                                        .Any(x => x.ColumnIndex == i.ColumnIndex));

var filteredIssues = formatIssues.Union(groupIssues).Except(dupeIssues);
person SteveB    schedule 11.05.2011
comment
хм... но это удаляет оба значения, удаление дубликатов обычно вы хотите сохранить одно из них. Я думаю, вы хотели сказать formatIssues.Union(groupIssues.Except(dupeIssues)); - person Simon_Weaver; 07.06.2015