C# Distinct() 与 lambda?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1300088/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 14:53:07  来源:igfitidea点击:

Distinct() with lambda?

c#c#-3.0lambdaextension-methods

提问by Tor Haugen

Right, so I have an enumerable and wish to get distinct values from it.

是的,所以我有一个可枚举的,并希望从中获得不同的值。

Using System.Linq, there's of course an extension method called Distinct. In the simple case, it can be used with no parameters, like:

使用System.Linq,当然有一个名为 的扩展方法Distinct。在简单的情况下,它可以不带参数使用,例如:

var distinctValues = myStringList.Distinct();

Well and good, but if I have an enumerable of objects for which I need to specify equality, the only available overload is:

很好,但是如果我有一个可枚举的对象,我需要为其指定相等性,那么唯一可用的重载是:

var distinctValues = myCustomerList.Distinct(someEqualityComparer);

The equality comparer argument must be an instance of IEqualityComparer<T>. I can do this, of course, but it's somewhat verbose and, well, cludgy.

相等比较器参数必须是 的实例IEqualityComparer<T>。我当然可以这样做,但它有点冗长,而且很笨拙。

What I would have expected is an overload that would take a lambda, say a Func<T, T, bool>:

我所期望的是一个使用 lambda 的重载,比如一个 Func<T, T, bool>:

var distinctValues
    = myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);

Anyone know if some such extension exists, or some equivalent workaround? Or am I missing something?

任何人都知道是否存在一些这样的扩展,或者一些等效的解决方法?或者我错过了什么?

Alternatively, is there a way of specifying an IEqualityComparer inline (embarass me)?

或者,有没有办法指定 IEqualityComparer 内联(让我难堪)?

Update

更新

I found a reply by Anders Hejlsberg to a postin an MSDN forum on this subject. He says:

我找到了 Anders Hejlsberg 对MSDN 论坛中有关此主题的帖子的回复。他说:

The problem you're going to run into is that when two objects compare equal they must have the same GetHashCode return value (or else the hash table used internally by Distinct will not function correctly). We use IEqualityComparer because it packages compatible implementations of Equals and GetHashCode into a single interface.

您将遇到的问题是,当两个对象比较相等时,它们必须具有相同的 GetHashCode 返回值(否则 Distinct 内部使用的哈希表将无法正常运行)。我们使用 IEqualityComparer,因为它将 Equals 和 GetHashCode 的兼容实现打包到单个接口中。

I suppose that makes sense..

我想这是有道理的..

采纳答案by Carlo Bos

IEnumerable<Customer> filteredList = originalList
  .GroupBy(customer => customer.CustomerId)
  .Select(group => group.First());

回答by Jon Skeet

It looks to me like you want DistinctByfrom MoreLINQ. You can then write:

在我看来,您希望DistinctByMoreLINQ 获得它。然后你可以写:

var distinctValues = myCustomerList.DistinctBy(c => c.CustomerId);

Here's a cut-down version of DistinctBy(no nullity checking and no option to specify your own key comparer):

这是DistinctBy(没有空值检查和指定您自己的密钥比较器的选项)的简化版本:

public static IEnumerable<TSource> DistinctBy<TSource, TKey>
     (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    HashSet<TKey> knownKeys = new HashSet<TKey>();
    foreach (TSource element in source)
    {
        if (knownKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}

回答by JaredPar

No there is no such extension method overload for this. I've found this frustrating myself in the past and as such I usually write a helper class to deal with this problem. The goal is to convert a Func<T,T,bool>to IEqualityComparer<T,T>.

不,没有这样的扩展方法重载。过去我发现这让自己很沮丧,因此我通常会编写一个帮助类来处理这个问题。目标是将 a 转换Func<T,T,bool>IEqualityComparer<T,T>.

Example

例子

public class EqualityFactory {
  private sealed class Impl<T> : IEqualityComparer<T,T> {
    private Func<T,T,bool> m_del;
    private IEqualityComparer<T> m_comp;
    public Impl(Func<T,T,bool> del) { 
      m_del = del;
      m_comp = EqualityComparer<T>.Default;
    }
    public bool Equals(T left, T right) {
      return m_del(left, right);
    } 
    public int GetHashCode(T value) {
      return m_comp.GetHashCode(value);
    }
  }
  public static IEqualityComparer<T,T> Create<T>(Func<T,T,bool> del) {
    return new Impl<T>(del);
  }
}

This allows you to write the following

这允许您编写以下内容

var distinctValues = myCustomerList
  .Distinct(EqualityFactory.Create((c1, c2) => c1.CustomerId == c2.CustomerId));

回答by MattH

I'm assuming you have an IEnumerable, and in your example delegate, you would like c1 and c2 to be referring to two elements in this list?

我假设您有一个 IEnumerable,并且在您的示例委托中,您希望 c1 和 c2 引用此列表中的两个元素?

I believe you could achieve this with a self join var distinctResults = from c1 in myList join c2 in myList on

我相信你可以通过 self join var distinctResults = from c1 in myList join c2 in myList on 来实现这一点

回答by Kleinux

Something I have used which worked well for me.

我用过的东西对我来说效果很好。

/// <summary>
/// A class to wrap the IEqualityComparer interface into matching functions for simple implementation
/// </summary>
/// <typeparam name="T">The type of object to be compared</typeparam>
public class MyIEqualityComparer<T> : IEqualityComparer<T>
{
    /// <summary>
    /// Create a new comparer based on the given Equals and GetHashCode methods
    /// </summary>
    /// <param name="equals">The method to compute equals of two T instances</param>
    /// <param name="getHashCode">The method to compute a hashcode for a T instance</param>
    public MyIEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
    {
        if (equals == null)
            throw new ArgumentNullException("equals", "Equals parameter is required for all MyIEqualityComparer instances");
        EqualsMethod = equals;
        GetHashCodeMethod = getHashCode;
    }
    /// <summary>
    /// Gets the method used to compute equals
    /// </summary>
    public Func<T, T, bool> EqualsMethod { get; private set; }
    /// <summary>
    /// Gets the method used to compute a hash code
    /// </summary>
    public Func<T, int> GetHashCodeMethod { get; private set; }

    bool IEqualityComparer<T>.Equals(T x, T y)
    {
        return EqualsMethod(x, y);
    }

    int IEqualityComparer<T>.GetHashCode(T obj)
    {
        if (GetHashCodeMethod == null)
            return obj.GetHashCode();
        return GetHashCodeMethod(obj);
    }
}

回答by Gordon Freeman

This will do what you want but I don't know about performance:

这会做你想做的,但我不知道性能:

var distinctValues =
    from cust in myCustomerList
    group cust by cust.CustomerId
    into gcust
    select gcust.First();

At least it's not verbose.

至少它不是冗长的。

回答by Andy Singh

If Distinct()doesn't produce unique results, try this one:

如果Distinct()没有产生独特的结果,请尝试以下方法:

var filteredWC = tblWorkCenter.GroupBy(cc => cc.WCID_I).Select(grp => grp.First()).Select(cc => new Model.WorkCenter { WCID = cc.WCID_I }).OrderBy(cc => cc.WCID); 

ObservableCollection<Model.WorkCenter> WorkCenter = new ObservableCollection<Model.WorkCenter>(filteredWC);

回答by David Kirkland

Here's a simple extension method that does what I need...

这是一个简单的扩展方法,可以满足我的需要...

public static class EnumerableExtensions
{
    public static IEnumerable<TKey> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector)
    {
        return source.GroupBy(selector).Select(x => x.Key);
    }
}

It's a shame they didn't bake a distinct method like this into the framework, but hey ho.

很遗憾他们没有将这样的独特方法烘焙到框架中,但是嘿嘿。

回答by Dmitry Ledentsov

All solutions I've seen here rely on selecting an already comparable field. If one needs to compare in a different way, though, this solution hereseems to work generally, for something like:

我在这里看到的所有解决方案都依赖于选择一个已经具有可比性的领域。但是,如果需要以不同的方式进行比较,这里的解决方案似乎普遍适用,例如:

somedoubles.Distinct(new LambdaComparer<double>((x, y) => Math.Abs(x - y) < double.Epsilon)).Count()

回答by Arasu RRK

Shorthand solution

速记解决方案

myCustomerList.GroupBy(c => c.CustomerId, (key, c) => c.FirstOrDefault());