繁体   English   中英

为什么没有Linq方法通过谓词返回不同的值?

[英]Why is there no Linq method to return distinct values by a predicate?

我想在列表中获取不同的值,但不是通过标准的相等比较。

我想做的是这样的:

return myList.Distinct( (x, y) => x.Url == y.Url );

我不能,Linq中没有扩展方法可以做到这一点 - 只需要一个IEqualityComparer

我可以用这个来解决它:

return myList.GroupBy( x => x.Url ).Select( g => g.First() );

但这似乎很混乱。 它也没有做同样的事情 - 我只能在这里使用它因为我有一把钥匙。

我也可以添加自己的:

public static IEnumerable<T> Distinct<T>( 
    this IEnumerable<T> input, Func<T,T,bool> compare )
{
    //write my own here
}

但这似乎就像写一些应该存在的东西一样。

任何人都知道为什么这种方法不存在?

我错过了什么吗?

当然,这很烦人。 它也是我的“MoreLINQ”项目的一部分,我必须在某些方面注意:)有很多其他操作在投影时有意义,但返回原始--MaxBy和MinBy春天的想法。

正如你所说,它很容易编写 - 虽然我更喜欢名称“DistinctBy”来匹配OrderBy等。如果您感兴趣,这是我的实现:

    public static IEnumerable<TSource> DistinctBy<TSource, TKey>
        (this IEnumerable<TSource> source,
         Func<TSource, TKey> keySelector)
    {
        return source.DistinctBy(keySelector,
                                 EqualityComparer<TKey>.Default);
    }

    public static IEnumerable<TSource> DistinctBy<TSource, TKey>
        (this IEnumerable<TSource> source,
         Func<TSource, TKey> keySelector,
         IEqualityComparer<TKey> comparer)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (keySelector == null)
        {
            throw new ArgumentNullException("keySelector");
        }
        if (comparer == null)
        {
            throw new ArgumentNullException("comparer");
        }
        return DistinctByImpl(source, keySelector, comparer);
    }

    private static IEnumerable<TSource> DistinctByImpl<TSource, TKey>
        (IEnumerable<TSource> source,
         Func<TSource, TKey> keySelector,
         IEqualityComparer<TKey> comparer)
    {
        HashSet<TKey> knownKeys = new HashSet<TKey>(comparer);
        foreach (TSource element in source)
        {
            if (knownKeys.Add(keySelector(element)))
            {
                yield return element;
            }
        }
    }

但这似乎很混乱。

它并不凌乱,这是正确的。

  • 如果你想要FirstName的Distinct Programmers并且有四个Amy,你想要哪一个?
  • 如果你按FirstName对程序员进行Group并取First一个,那么在四个Amy的情况下你很清楚你想要做什么。

我只能在这里使用它因为我有一把钥匙。

您可以使用相同的模式执行多个“不同”键:

return myList
  .GroupBy( x => new { x.Url, x.Age } )
  .Select( g => g.First() );

乔恩,你的解决方案非常好。 虽然有一个小的改变。 我认为我们不需要EqualityComparer.Default。 这是我的解决方案(当然,起点是Jon Skeet的解决方案)

    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
    {
        //TODO All arg checks
        HashSet<TKey> keys = new HashSet<TKey>();
        foreach (T item in source)
        {
            TKey key = keySelector(item);
            if (!keys.Contains(key))
            {
                keys.Add(key);
                yield return item;
            }
        }
    }

使用AmyB的答案 ,我编写了一个小的DistinctBy扩展方法,以允许传递谓词:

/// <summary>
/// Distinct method that accepts a perdicate
/// </summary>
/// <typeparam name="TSource">The type of the t source.</typeparam>
/// <typeparam name="TKey">The type of the t key.</typeparam>
/// <param name="source">The source.</param>
/// <param name="predicate">The predicate.</param>
/// <returns>IEnumerable&lt;TSource&gt;.</returns>
/// <exception cref="System.ArgumentNullException">source</exception>
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
    (this IEnumerable<TSource> source,
     Func<TSource, TKey> predicate)
{
    if (source == null)
        throw new ArgumentNullException("source");

    return source
        .GroupBy(predicate)
        .Select(x => x.First());
}

您现在可以通过以下方式传递谓词以对列表进行分组:

var distinct = myList.DistinctBy(x => x.Id);

或按多个属性分组:

var distinct = myList.DistinctBy(x => new { x.Id, x.Title });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM