代码之家  ›  专栏  ›  技术社区  ›  James Manning

‘distinct by delegate’的“better”(更简单、更快,随便什么)版本?

  •  2
  • James Manning  · 技术社区  · 14 年前

    我讨厌贴这个,因为它有点主观,但感觉有一个更好的方法来做这件事,我只是没有想到。

    请注意,此扩展没有采用 IEqualityComparer<T> 因为这样的事情可以做更复杂的比较逻辑,但这就是我现在所需要的:)

    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> getKeyFunc)
    {
        return from s in source
                group s by getKeyFunc(s) into sourceGroups
                select sourceGroups.First();
    }
    

    用法示例:

    var items = new[]
    {
        new { A = 1, B = "foo", C = Guid.NewGuid(), },
        new { A = 2, B = "foo", C = Guid.NewGuid(), },
        new { A = 1, B = "bar", C = Guid.NewGuid(), },
        new { A = 2, B = "bar", C = Guid.NewGuid(), },
    };
    
    var itemsByA = items.DistinctBy(item => item.A).ToList();
    var itemsByB = items.DistinctBy(item => item.B).ToList();
    
    2 回复  |  直到 14 年前
        1
  •  2
  •   LukeH    14 年前

    干得好。我不认为这比你自己的版本效率更高,但它应该有一点优势。它只需要一次通过序列, yield

    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
        this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        return source.DistinctBy(keySelector, null);
    }
    
    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer)
    {
        if (source == null)
            throw new ArgumentNullException("source");
    
        if (keySelector == null)
            throw new ArgumentNullException("keySelector");
    
        return source.DistinctByIterator(keySelector, keyComparer);
    }
    
    private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> keyComparer)
    {
        var keys = new HashSet<TKey>(keyComparer);
    
        foreach (TSource item in source)
        {
            if (keys.Add(keySelector(item)))
                yield return item;
        }
    }
    
        2
  •  2
  •   LBushkin    14 年前

    我以前写过一个 Func => IEqualityComparer 实用程序类只是为了能够调用接受 IEqualityComparer

    public static class ComparerExt
    {
        private class GenericEqualityComparer<T> : IEqualityComparer<T>
        {
            private readonly Func<T, T, bool> m_CompareFunc;
    
            public GenericEqualityComparer( Func<T,T,bool> compareFunc ) {
                m_CompareFunc = compareFunc;
            }
    
            public bool Equals(T x, T y) {
                return m_CompareFunc(x, y);
            }
    
            public int GetHashCode(T obj) {
                return obj.GetHashCode(); // don't override hashing semantics
            }
        }
    
        public static IComparer<T> Compare<T>( Func<T,T,bool> compareFunc ) {
            return new GenericEqualityComparer<T>(compareFunc);
        }
    }
    

    您可以这样使用:

    var result = list.Distinct( ComparerExt.Compare( (a,b) => { /*whatever*/ } );
    

    我也经常投一个 Reverse() 方法以允许更改比较中操作数的顺序,如下所示:

    private class GenericComparer<T> : IComparer<T>
    {
        private readonly Func<T, T, int> m_CompareFunc;
        public GenericComparer( Func<T,T,int> compareFunc ) {
            m_CompareFunc = compareFunc;
        }
        public int Compare(T x, T y) {
            return m_CompareFunc(x, y);
        }
    }
    
    public static IComparer<T> Reverse<T>( this IComparer<T> comparer )
    {
        return new GenericComparer<T>((a, b) => comparer.Compare(b, a));
    }