使用 IEqualityComparer 进行联合

Posted

技术标签:

【中文标题】使用 IEqualityComparer 进行联合【英文标题】:Using IEqualityComparer for Union 【发布时间】:2011-05-11 19:26:35 【问题描述】:

我只是想从两个列表中删除重复项并将它们合并到一个列表中。我还需要能够定义重复项是什么。我通过 ColumnIndex 属性定义了一个副本,如果它们相同,则它们是重复的。这是我采取的方法:

我找到了一个很好的例子,说明如何为代码段中只需要一次 em 的随机场合编写内联比较器。

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 属性。

【问题讨论】:

只是为了了解这个问题的背景知识,您是否尝试过调试代码并确定调用的是 public bool Equals(T x, T y) 方法而不是 public int GetHashCode(T obj) 方法? 结果真的是null,不是空序列?这真的很奇怪,因为Enumerable.Union() 永远不应该返回null 【参考方案1】:

我刚刚在测试集上运行了您的代码....它可以工作!

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

那么...也许回去检查您的测试数据 - 或者使用其他一些测试数据运行它?

毕竟 - 联合为空 - 这表明您的两个输入列表也是空的?

【讨论】:

你是对的。我加倍检查了列索引,没有一个是相等的。布赖恩放屁时刻。我需要根据 Id 列进行比较。这帮助我克服了我的白痴障碍,因此得到了答案投票=P【参考方案2】:

稍微简单一点的方法:

它确实保留了原始顺序 它会在发现骗子时忽略它们

使用链接扩展方法:

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

这是来自MoreLinqDistinctBy lambda 方法

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;
        
    

【讨论】:

很好的解决方案。这比其他方法更简单。【参考方案3】:

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

【讨论】:

urmm... 但这会删除这两个值,重复删除通常您希望保留其中一个。我想你的意思是说formatIssues.Union(groupIssues.Except(dupeIssues));

以上是关于使用 IEqualityComparer 进行联合的主要内容,如果未能解决你的问题,请参考以下文章

通用 IEqualityComparer 使用反射和属性来标记我想要比较的内容

List Except 操作,IEqualityComparer 使用

在 IEqualityComparer 中包装委托

如何实现 IEqualityComparer 以返回不同的值?

c#List结合IEqualityComparer求交集

有没有办法从 IComparer 派生 IEqualityComparer?