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

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

我想要做的是这样的:

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弹簧。

正如你所说,很容易写 – 尽pipe我更喜欢“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; } } } 

但是,这似乎凌乱。

这不是混乱,这是正确的。

  • 如果你想要名字Distinct程序员,并且有四个David,你想要哪一个?
  • 如果你把程序员按照名字Group并采取First一个,那么你很清楚你想在四个大卫的情况下做什么。

我只能在这里使用,因为我有一个键。

你可以用相同的模式做一个多重的“不同的”

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

乔恩,你的解决scheme是相当不错的。 一个小的变化,但。 那里我不认为我们需要EqualityComparer.Default。 这是我的解决scheme(当然,起点是Jon Skeet的解决scheme)

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

使用@DavidB的答案 ,我写了一个小的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 });