Distinct()与lambda?

元气小坏坏 提交于 2019-12-23 21:37:26

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

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

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

var distinctValues = myStringList.Distinct();

好的,但如果我有一个可以指定相等性的可枚举对象,唯一可用的重载是:

var distinctValues = myCustomerList.Distinct(someEqualityComparer);

equality comparer参数必须是IEqualityComparer<T>的实例。 当然,我可以做到这一点,但它有点冗长,而且很有说服力。

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

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

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

或者,有没有一种方法可以指定IEqualityComparer内联(embarass me)?

更新

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

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

我认为那是有道理的..


#1楼

它看起来像你想要来自MoreLINQ的 DistinctBy 。 然后你可以写:

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

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

#2楼

没有这样的扩展方法重载。 我在过去发现这令人沮丧,因此我经常写一个助手类来处理这个问题。 目标是将Func<T,T,bool>IEqualityComparer<T,T>

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

这允许您编写以下内容

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

#3楼

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

我相信你可以通过自我连接var distinctResults =来自myList中的myList join c2中的c1来实现这一点


#4楼

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

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

#5楼

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

somedoubles.Distinct(new LambdaComparer<double>((x, y) => Math.Abs(x - y) < double.Epsilon)).Count()
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!