我想在列表中获取不同的值,但不是通过标准的相等比较。
我想做的是这样的事情:
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
}
但这似乎就像写一些应该存在的东西一样。
任何人都知道为什么这种方法不存在?
我错过了什么吗?
答案 0 :(得分:51)
正如你所说,它很容易写 - 虽然我更喜欢名字“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;
}
}
}
答案 1 :(得分:32)
但这似乎很混乱。
它并不凌乱,这是正确的。
Distinct
程序员并且有四个大卫,你想要哪一个?Group
程序员通过FirstName并取First
个程序员,那么很明显您想要在四个David的情况下做什么。我只能在这里使用它,因为我只有一把钥匙。
您可以使用相同的模式执行多个“不同”键:
return myList
.GroupBy( x => new { x.Url, x.Age } )
.Select( g => g.First() );
答案 2 :(得分:3)
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;
}
}
}
答案 3 :(得分:1)
使用@DavidB&#39; answer,我已经编写了一个小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<TSource>.</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 });