Pregunta Distinto () con lambda?


Correcto, entonces tengo un enumerable y deseo obtener valores distintos de él.

Utilizando System.Linq, por supuesto, se llama un método de extensión Distinct. En el caso simple, se puede usar sin parámetros, como:

var distinctValues = myStringList.Distinct();

Bien y bien, pero si tengo un enumerable de objetos para los cuales necesito especificar la igualdad, la única sobrecarga disponible es:

var distinctValues = myCustomerList.Distinct(someEqualityComparer);

El argumento del comparador de igualdad debe ser una instancia de IEqualityComparer<T>. Puedo hacer esto, por supuesto, pero es un tanto detallado y, bueno, torpe.

Lo que habría esperado es una sobrecarga que tomaría una lambda, digamos un Func <T, T, bool>:

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

¿Alguien sabe si existe alguna de esas extensiones, o alguna solución equivalente? ¿O me estoy perdiendo algo?

Alternativamente, ¿hay alguna manera de especificar un IEqualityComparer en línea (embarass me)?

Actualizar

Encontré una respuesta de Anders Hejlsberg a un enviar en un foro de MSDN sobre este tema. Él dice:

El problema con el que se encontrará es que cuando dos objetos se comparan   iguales deben tener el mismo valor de retorno GetHashCode (o bien el   la tabla hash usada internamente por Distinct no funcionará correctamente).   Usamos IEqualityComparer porque paquetes compatibles   implementaciones de Equals y GetHashCode en una sola interfaz.

Supongo que tiene sentido..


625
2017-08-19 13:50


origen


Respuestas:


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

878
2017-11-11 19:19



Me parece que quieres DistinctBy de MásLINQ. Luego puedes escribir:

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

Aquí hay una versión reducida de DistinctBy (sin verificación de nulidad y sin opción para especificar su propio comparador de claves):

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

415
2017-08-19 13:55



Para terminar las cosas . Creo que la mayoría de la gente que vino aquí como yo quiere la más simple solución posible sin usar ninguna biblioteca y con el mejor posible actuación.

(El grupo aceptado por método para mí, creo que es una exageración en términos de rendimiento).

Aquí hay un método de extensión simple que usa IEqualityComparer interfaz que también funciona para valores nulos.

Uso:

var filtered = taskList.DistinctBy(t => t.TaskExternalId).ToArray();

Código de método de extensión

public static class LinqExtensions
{
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
    {
        GeneralPropertyComparer<T, TKey> comparer = new GeneralPropertyComparer<T,TKey>(property);
        return items.Distinct(comparer);
    }   
}
public class GeneralPropertyComparer<T,TKey> : IEqualityComparer<T>
{
    private Func<T, TKey> expr { get; set; }
    public GeneralPropertyComparer (Func<T, TKey> expr)
    {
        this.expr = expr;
    }
    public bool Equals(T left, T right)
    {
        var leftProp = expr.Invoke(left);
        var rightProp = expr.Invoke(right);
        if (leftProp == null && rightProp == null)
            return true;
        else if (leftProp == null ^ rightProp == null)
            return false;
        else
            return leftProp.Equals(rightProp);
    }
    public int GetHashCode(T obj)
    {
        var prop = expr.Invoke(obj);
        return (prop==null)? 0:prop.GetHashCode();
    }
}

20
2017-12-09 13:48



No, no hay una sobrecarga de método de extensión para esto. Me he encontrado frustrante en el pasado y, como tal, suelo escribir una clase de ayuda para tratar 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 lo siguiente

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

19
2017-08-19 14:01



Solución abreviada

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

13
2017-08-20 13:27



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

Al menos no es detallado.


12
2017-10-06 14:41



Aquí hay un método de extensión simple que hace lo que necesito ...

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

Es una pena que no hayan horneado un método distinto como este en el marco, pero hola ho.


9
2018-03-01 20:19