'更好'(更简单,更快,无论如何)'版本'代表'?

时间:2010-07-15 20:35:05

标签: c# linq

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

有时我想通过某些列/属性“区分”一个集合,但不丢弃其他列(是的,这确实会丢失信息,因为它会变成任意的,你最终会得到哪些其他列的值)。 / p>

请注意,此扩展程序的功能不如采用IEqualityComparer<T>的Distinct重载,因为这样的事情可以执行更复杂的比较逻辑,但这就是我现在所需要的:)

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 个答案:

答案 0 :(得分:2)

我之前编写了一个通用Func => IEqualityComparer实用程序类,目的是为了能够调用接受IEqualityComparer的LINQ方法的重载,每次都必须编写一个自定义类。

它使用委托(就像你的例子一样)来提供比较语义。这允许我使用库方法的内置实现而不是自己编译 - 我认为这更可能是正确和有效实现的。

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

答案 1 :(得分:2)

你走了。我不认为这比你自己的版本更有效,但它应该有一点点优势。它只需要一次通过序列,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;
    }
}