434 votos

¿ForUpdate() con lambda?

A la derecha, así que tengo un enumerable y desea obtener los valores distintos de ella.

Utilizando System.Linq, hay, por supuesto, un método de extensión llamados Distinct. En el caso simple, que puede ser utilizado sin parámetros, como:

var distinctValues = myStringList.Distinct();

Bien y de buenas, pero si tengo un enumerable de objetos para el cual necesito para especificar la igualdad, la sobrecarga es:

var distinctValues = myCustomerList.Distinct(someEqualityComparer);

El comparador de igualdad argumento debe ser una instancia de IEqualityComparer<T>. Puedo hacer esto, por supuesto, pero es algo detallado y, así, cludgy.

Lo que yo hubiera esperado una sobrecarga que llevaría a una lambda, decir una Func<T, T, bool>:

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

Alguien sabe si algunos de esos extensión existe, o algún equivalente solución? O me estoy perdiendo algo?

Alternativamente, hay una manera de especificar una IEqualityComparer en línea (embarass mí)?

Actualización

He encontrado una respuesta por Anders Hejlsberg a un post en un foro de MSDN sobre este tema. Él dice:

El problema que vas a chocar es que cuando dos objetos a comparar igual deben tener el mismo GetHashCode valor de retorno (o de lo contrario el tabla de hash utilizado internamente por Distintas que no funcione correctamente). Utilizamos IEqualityComparer porque los paquetes compatibles las implementaciones de igual a Igual y GetHashCode en una sola interfaz.

Supongo que tiene sentido..

641voto

Carlo Bos Puntos 4948
myCustomerList.GroupBy(cust => cust.CustomerId).Select(grp => grp.First());

296voto

Jon Skeet Puntos 692016

Me parece a mí que quieres DistinctBy de MoreLINQ. Luego puede escribir:

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

Aquí hay una versión de DistinctBy (ninguna comprobación de nulidad y ninguna opción para especificar su propio comparador clave):

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

18voto

JaredPar Puntos 333733

No hay ninguna tal sobrecarga del método de la extensión para esto. He encontrado este mismo frustrante en el pasado y como tal suele escribir una clase auxiliar para lidiar con este problema. El objetivo es convertir un Func<T,T,bool> a IEqualityComparer<T,T> .

Ejemplo

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

Esto le permite escribir los siguientes

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

11voto

Gordon Freeman Puntos 99

Esto hará lo que quieras, pero no sé sobre el rendimiento:

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

Por lo menos no es detallado.

4voto

Kleinux Puntos 896

Algo he usado que funcionó bien para mí.

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

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X