Pregunta Distinct de LINQ () en una propiedad particular


Estoy jugando con LINQ para aprender sobre esto, pero no puedo entender cómo usar Distinct cuando no tengo una lista simple (una simple lista de enteros es bastante fácil de hacer, esta no es la pregunta). Lo que si quiero usar Distinto en una lista de un objeto en uno o Más propiedades del objeto?

Ejemplo: si un objeto es Person, con propiedad Id. ¿Cómo puedo obtener toda la persona y el uso Distinct en ellos con la propiedad Id del objeto?

Person1: Id=1, Name="Test1"
Person2: Id=1, Name="Test1"
Person3: Id=2, Name="Test2"

¿Cómo puedo obtener solo Person1 y Person3? ¿Es eso posible?

Si no es posible con LINQ, ¿cuál sería la mejor manera de tener una lista de Person dependiendo de algunas de sus propiedades en .NET 3.5?


775
2018-01-28 20:45


origen


Respuestas:


EDITAR: Esto ahora es parte de MásLINQ.

Lo que necesita es un "distinct-by" eficazmente. No creo que sea parte de LINQ tal como está, aunque es bastante fácil de escribir:

public static IEnumerable<TSource> DistinctBy<TSource, TKey>
    (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    HashSet<TKey> seenKeys = new HashSet<TKey>();
    foreach (TSource element in source)
    {
        if (seenKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}

Entonces, para encontrar los distintos valores usando solo el Id propiedad, podrías usar:

var query = people.DistinctBy(p => p.Id);

Y para usar propiedades múltiples, puede usar tipos anónimos, que implementan la igualdad de manera apropiada:

var query = people.DistinctBy(p => new { p.Id, p.Name });

No probado, pero debería funcionar (y ahora al menos compila).

Sin embargo, asume el comparador predeterminado para las teclas: si desea pasar un comparador de igualdad, simplemente páselo a la HashSet constructor.


925
2018-01-28 21:17



¿Qué sucede si quiero obtener una lista distinta basada en uno o Más propiedades?

¡Sencillo! Desea agruparlos y elegir un ganador del grupo.

List<Person> distinctPeople = allPeople
  .GroupBy(p => p.PersonId)
  .Select(g => g.First())
  .ToList();

Si desea definir grupos en propiedades múltiples, aquí se muestra cómo:

List<Person> distinctPeople = allPeople
  .GroupBy(p => new {p.PersonId, p.FavoriteColor} )
  .Select(g => g.First())
  .ToList();

1456
2018-01-29 14:39



También puede usar la sintaxis de consulta si desea que se vea como LINQ-like:

var uniquePeople = from p in people
                   group p by new {p.ID} //or group by new {p.ID, p.Name, p.Whatever}
                   into mygroup
                   select mygroup.FirstOrDefault();

61
2018-03-06 18:28



Creo que es suficiente

list.Select(s => s.MyField).Distinct();

47
2018-01-23 14:54



Utilizar:

List<Person> pList = new List<Person>();
/* Fill list */

var result = pList.Where(p => p.Name != null).GroupBy(p => p.Id).Select(grp => grp.FirstorDefault());

los where te ayuda a filtrar las entradas (podría ser más complejo) y groupby y select realizar la función distinta.


36
2018-02-14 12:52



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ón

Persons.ToLookup(p => p.Id).Select(coll => coll.First());

18
2018-01-20 15:01



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.

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
  this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
  HashSet<TKey> seenKeys = new HashSet<TKey>();
  return source.Where(element => seenKeys.Add(keySelector(element)));
}

Incidentially, echa un vistazo La última versión de Jon Skeet de DistinctBy.cs en Google Code.


15
2018-02-06 19:56