Estoy jugando con LINQ para aprenderlo, pero no puedo entender cómo usarlo Distinct
cuando no tengo una lista simple (una lista simple de enteros es bastante fácil de hacer, esta no es la pregunta). ¿Qué debo hacer si quiero usar Distinct en una lista de un Objeto en una o más propiedades del objeto?
Ejemplo: si un objeto es Person
, con Propiedad Id
. ¿Cómo puedo obtener toda la Persona y usarla Distinct
con la propiedad Id
del objeto?
Person1: Id=1, Name="Test1"
Person2: Id=1, Name="Test1"
Person3: Id=2, Name="Test2"
¿Cómo puedo ser justo Person1
y Person3
? ¿Es eso posible?
Si no es posible con LINQ, ¿cuál sería la mejor manera de tener una lista Person
dependiendo de algunas de sus propiedades en .NET 3.5?
GroupBy
es más sencillo. Si lo necesita en más de un lugar, es mucho más limpio (IMO) encapsular la intención.IQueryable<T>
aquí, no veo cómo es relevante. Estoy de acuerdo en que esto no sería adecuado para EF, etc., pero dentro de LINQ to Objects creo que es más adecuado queGroupBy
. El contexto de la pregunta siempre es importante.¡Simple! Desea agruparlos y elegir un ganador del grupo.
Si desea definir grupos en varias propiedades, aquí le mostramos cómo:
fuente
Single()
ySingleOrDefault()
cada lanzamiento cuando la fuente tiene más de un elemento. En esta operación, esperamos la posibilidad de que cada grupo tenga más de un elemento. Para el caso,First()
es preferible en lugar deFirstOrDefault()
porque cada grupo debe tener al menos un miembro ... a menos que esté utilizando EntityFramework, que no puede darse cuenta de que cada grupo tiene al menos un miembro y las demandasFirstOrDefault()
.FirstOrDefault()
github.com/dotnet/efcore/issues/12088 Estoy en 3.1, y recibo errores "incapaces de traducir".Utilizar:
Lo
where
ayuda a filtrar las entradas (podría ser más complejo)groupby
y aselect
realizar la función distinta.fuente
También puede usar la sintaxis de consulta si desea que se vea como LINQ:
fuente
Creo que es suficiente:
fuente
Solución, primero agrupe por sus campos, luego seleccione el primer elemento predeterminado.
fuente
Puedes hacer esto con el estándar
Linq.ToLookup()
. Esto creará una colección de valores para cada clave única. Solo selecciona el primer elemento de la colecciónfuente
El siguiente código es funcionalmente equivalente a la respuesta de Jon Skeet .
Probado en .NET 4.5, debería funcionar en cualquier versión anterior de LINQ.
Incidentalmente, vea la última versión de Jon Skeet de DistinctBy.cs en Google Code .
fuente
Escribí un artículo que explica cómo extender la función Distinct para que pueda hacer lo siguiente:
Aquí está el artículo: Ampliación de LINQ: especificación de una propiedad en la función distinta
fuente
Personalmente uso la siguiente clase:
Entonces, un método de extensión:
Finalmente, el uso previsto:
La ventaja que encontré al usar este enfoque es la reutilización de la
LambdaEqualityComparer
clase para otros métodos que aceptan unIEqualityComparer
. (Ah, y dejo lasyield
cosas a la implementación original de LINQ ...)fuente
En caso de que necesite un método distinto en varias propiedades, puede consultar mi biblioteca de PowerfulExtensions . Actualmente se encuentra en una etapa muy joven, pero ya puede utilizar métodos como Distinct, Union, Intersect, Except en cualquier cantidad de propiedades;
Así es como lo usas:
fuente
Cuando enfrentamos tal tarea en nuestro proyecto, definimos una pequeña API para componer comparadores.
Entonces, el caso de uso fue así:
Y la API en sí se ve así:
Más detalles se encuentran en nuestro sitio: IEqualityComparer en LINQ .
fuente
Puede usar DistinctBy () para obtener registros distintos por una propiedad de objeto. Simplemente agregue la siguiente declaración antes de usarla:
y luego úsalo como sigue:
donde 'Índice' es la propiedad en la que quiero que los datos sean distintos.
fuente
Puede hacerlo (aunque no a la velocidad del rayo) así:
Es decir, "seleccione a todas las personas donde no haya otra persona diferente en la lista con la misma ID".
Tenga en cuenta, en su ejemplo, que solo seleccionaría a la persona 3. No estoy seguro de cómo decir cuál quiere, de los dos anteriores.
fuente
Si no desea agregar la biblioteca MoreLinq a su proyecto solo para obtener la
DistinctBy
funcionalidad, puede obtener el mismo resultado final utilizando la sobrecarga delDistinct
método de Linq que toma unIEqualityComparer
argumento.Comienza creando una clase de comparación de igualdad personalizada genérica que utiliza la sintaxis lambda para realizar una comparación personalizada de dos instancias de una clase genérica:
Luego, en su código principal, lo usa así:
Voila! :)
Lo anterior supone lo siguiente:
Person.Id
es de tipoint
people
colección no contiene ningún elemento nulo.Si la colección podría contener nulos, simplemente reescriba las lambdas para verificar si hay nulos, por ejemplo:
EDITAR
Este enfoque es similar al de la respuesta de Vladimir Nesterovsky pero más simple.
También es similar al de la respuesta de Joel, pero permite una lógica de comparación compleja que involucra múltiples propiedades.
Sin embargo, si sus objetos solo pueden diferir para
Id
entonces, otro usuario dio la respuesta correcta de que todo lo que necesita hacer es anular las implementaciones predeterminadas deGetHashCode()
yEquals()
en suPerson
clase y luego simplemente usar elDistinct()
método listo para usar de Linq para filtrar fuera cualquier duplicado.fuente
La mejor manera de hacer esto que será compatible con otras versiones de .NET es anular Equals y GetHash para manejar esto (consulte la pregunta sobre desbordamiento de pila. Este código devuelve valores distintos. Sin embargo, lo que quiero es devolver una colección fuertemente tipada en lugar de un tipo anónimo ), pero si necesita algo genérico en todo el código, las soluciones en este artículo son excelentes.
fuente
fuente
Select()
new Person
lugar denew Player
? Sin embargo, el hecho de que esté ordenandoID
no informa de alguna manera elDistinct()
uso de esa propiedad para determinar la unicidad, por lo que esto no funcionará.Métodos Override Equals (object obj) y GetHashCode () :
y luego solo llama:
fuente
Debería poder anular Equals en persona para hacer Equals en Person.id. Esto debería resultar en el comportamiento que buscas.
fuente
Por favor intente con el siguiente código.
fuente