Bien, entonces tengo un enumerable y deseo obtener valores distintos de él.
Usando System.Linq
, por supuesto, hay un método de extensión llamado Distinct
. En el caso simple, se puede usar sin parámetros, como:
var distinctValues = myStringList.Distinct();
Bien y bien, pero si tengo un número de objetos para los que necesito especificar 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 algo detallado y, bueno, deslucido.
Lo que hubiera esperado es una sobrecarga que tomaría una lambda, digamos una Func <T, T, bool>:
var distinctValues
= myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);
¿Alguien sabe si existe alguna extensión de este tipo, o alguna solución alternativa equivalente? ¿O me estoy perdiendo algo?
Alternativamente, ¿hay alguna forma de especificar un IEqualityComparer en línea (me avergüenza)?
Actualizar
Encontré una respuesta de Anders Hejlsberg a una publicación 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 la tabla hash utilizada internamente por Distinct no funcionará correctamente). Utilizamos IEqualityComparer porque empaqueta implementaciones compatibles de Equals y GetHashCode en una sola interfaz.
Supongo que tiene sentido..
fuente
.Distinct(new KeyEqualityComparer<Customer,string>(c1 => c1.CustomerId))
, y explicar por qué GetHashCode () es importante para que funcione correctamente.Respuestas:
fuente
DistinctBy
(o inclusoDistinct
, ya que la firma será única).yield
declaración, por lo que técnicamente no es posible la transmisión. Gracias por tu respuesta sin embargo. Lo usaré cuando codifique en C #. ;-)Me parece que quieres
DistinctBy
de MoreLINQ . Entonces puedes escribir:Aquí hay una versión reducida de
DistinctBy
(sin verificación de nulidad y sin opción para especificar su propio comparador de claves):fuente
yield
lib extra +, foreach puede reescribirse comoreturn source.Where(element => knownKeys.Add(keySelector(element)));
Para terminar las cosas . Creo que la mayoría de las personas que vinieron aquí como yo quieren la solución más simple posible sin usar bibliotecas y con el mejor rendimiento posible .
(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 la interfaz IEqualityComparer que funciona también para valores nulos.
Uso:
Código de método de extensión
fuente
No, no existe tal sobrecarga del método de extensión para esto. He encontrado esto frustrante en el pasado y, como tal, generalmente escribo una clase auxiliar para tratar este problema. El objetivo es convertir un
Func<T,T,bool>
aIEqualityComparer<T,T>
.Ejemplo
Esto le permite escribir lo siguiente
fuente
IEqualityComparer<T>
desde una proyección: stackoverflow.com/questions/188120/…Solución abreviada
fuente
Esto hará lo que quieras pero no sé sobre rendimiento:
Al menos no es detallado.
fuente
Aquí hay un método de extensión simple que hace lo que necesito ...
Es una pena que no hayan preparado un método distinto como este en el marco, pero bueno, ho
fuente
x.Key
ax.First()
y cambiar el valor de retorno aIEnumerable<T>
Algo que he usado y que me funcionó bien.
fuente
Todas las soluciones que he visto aquí se basan en seleccionar un campo ya comparable. Sin embargo, si uno necesita comparar de una manera diferente, esta solución aquí parece funcionar en general, para algo como:
fuente
Toma otra forma:
La secuencia devuelve elementos distintos y los compara por propiedad '_myCaustomerProperty'
fuente
Puedes usar InlineComparer
Muestra de uso :
Fuente: https://stackoverflow.com/a/5969691/206730
Uso de IEqualityComparer para Union
¿Puedo especificar mi comparador de tipos explícito en línea?
fuente
Puede usar LambdaEqualityComparer:
fuente
Una forma complicada de hacer esto es usar la
Aggregate()
extensión, usando un diccionario como acumulador con los valores de las propiedades clave como claves:Y una solución de estilo GroupBy está utilizando
ToLookup()
:fuente
Dictionary<int, Customer>
en su lugar?Supongo que tiene un IEnumerable, y en su delegado de ejemplo, ¿le gustaría que c1 y c2 se refieran a dos elementos en esta lista?
Creo que podría lograr esto con una autounión var distinctResults = de c1 en myList únete c2 en myList en
fuente
Si
Distinct()
no produce resultados únicos, prueba este:fuente
El paquete Microsoft System.Interactive tiene una versión de Distinct que toma un selector de clave lambda. Esto es efectivamente lo mismo que la solución de Jon Skeet, pero puede ser útil que la gente sepa y revise el resto de la biblioteca.
fuente
Así es como puedes hacerlo:
Este método le permite usarlo especificando un parámetro como
.MyDistinct(d => d.Name)
, pero también le permite especificar una condición de tener como segundo parámetro de esta manera:NB Esto también le permitiría especificar otras funciones como, por ejemplo
.LastOrDefault(...)
, también.Si desea exponer solo la condición, puede hacerlo aún más simple al implementarlo como:
En este caso, la consulta se vería así:
NB Aquí, la expresión es más simple, pero la nota se
.MyDistinct2
usa.FirstOrDefault(...)
implícitamente.Nota: Los ejemplos anteriores están utilizando la siguiente clase de demostración
fuente
IEnumerable
extensión lambda:Uso:
fuente