我讨厌发布这个,因为它有点主观,但感觉有一个更好的方法来做到这一点,我只是没想到。
有时我想通过某些列/属性“区分”一个集合,但不丢弃其他列(是的,这确实会丢失信息,因为它会变成任意的,你最终会得到哪些其他列的值)。 / 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();
答案 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;
}
}