c# - example - Distintivo de LINQ() en una propiedad particular




select distinct linq c# example (13)

Estoy jugando con LINQ para aprender sobre esto, pero no puedo entender cómo usar Distinct cuando no tengo una lista simple (una lista simple de enteros es bastante fácil de hacer, no es la pregunta). ¿Qué debo hacer si deseo usar Distinct en una lista de un objeto en una o más propiedades del objeto?

Ejemplo: Si un objeto es Person , con Id . De Propiedad. ¿Cómo puedo obtener toda la Persona y usar Distinct en ellos con el Id propiedad 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?


¿Qué sucede si deseo obtener una lista distinta basada en una o más propiedades?

¡Sencillo! Quieres 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 múltiples propiedades, aquí tiene cómo:

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

Creo que es suficiente:

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

Debería poder anular Equals on person para realmente hacer Equals en Person.id. Esto debería resultar en el comportamiento que estás buscando.


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

Incidencialmente, consulte code.google.com/p/morelinq/source/browse/MoreLinq/DistinctBy.cs .


He escrito un artículo que explica cómo extender la función Distinct para que pueda hacer lo siguiente:

var people = new List<Person>();

people.Add(new Person(1, "a", "b"));
people.Add(new Person(2, "c", "d"));
people.Add(new Person(1, "a", "b"));

foreach (var person in people.Distinct(p => p.ID))
    // Do stuff with unique list here.

Aquí está el artículo: Extendiendo LINQ - Especificando una propiedad en la función distinta


La mejor manera de hacer esto que sea compatible con otras versiones de .NET es reemplazar a Equals y GetHash para manejar esto (vea la pregunta sobre el desbordamiento de pila). Este código devuelve valores distintos. un tipo anónimo ), pero si necesita algo que sea genérico en todo su código, las soluciones en este artículo son excelentes.


Por favor, pruébalo con el siguiente código.

var Item = GetAll().GroupBy(x => x .Id).ToList();

Puedes hacer esto con el estándar Linq.ToLookup() . Esto creará una colección de valores para cada clave única. Solo selecciona el primer artículo de la colección.

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

Reemplace los métodos Equals (obj) y GetHashCode () :

class Person
{
    public int Id { get; set; }
    public int Name { get; set; }

    public override bool Equals(object obj)
    {
        return ((Person)obj).Id == Id;
        // or: 
        // var o = (Person)obj;
        // return o.Id == Id && o.Name == Name;
    }
    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

y luego simplemente llame:

List<Person> distinctList = new[] { person1, person2, person3 }.Distinct().ToList();

Si no desea agregar la biblioteca MoreLinq a su proyecto solo para obtener la funcionalidad DistinctBy , puede obtener el mismo resultado final utilizando la sobrecarga del método Distinct de Linq que IEqualityComparer un argumento de IEqualityComparer .

Comenzará por crear una clase de comparador de igualdad personalizada genérica que utiliza la sintaxis lambda para realizar una comparación personalizada de dos instancias de una clase genérica:

public class CustomEqualityComparer<T> : IEqualityComparer<T>
{
    Func<T, T, bool> _comparison;
    Func<T, int> _hashCodeFactory;

    public CustomEqualityComparer(Func<T, T, bool> comparison, Func<T, int> hashCodeFactory)
    {
        _comparison = comparison;
        _hashCodeFactory = hashCodeFactory;
    }

    public bool Equals(T x, T y)
    {
        return _comparison(x, y);
    }

    public int GetHashCode(T obj)
    {
        return _hashCodeFactory(obj);
    }
}

Luego en tu código principal lo usas así:

Func<Person, Person, bool> areEqual = (p1, p2) => int.Equals(p1.Id, p2.Id);

Func<Person, int> getHashCode = (p) => p.Id.GetHashCode();

var query = people.Distinct(new CustomEqualityComparer<Person>(areEqual, getHashCode));

Voila! :)

Lo anterior asume lo siguiente:

  • Propiedad Person.Id es de tipo int
  • La colección de people no contiene ningún elemento nulo.

Si la colección puede contener nulos, simplemente reescriba las lambdas para verificar si hay nulos, por ejemplo:

Func<Person, Person, bool> areEqual = (p1, p2) => 
{
    return (p1 != null && p2 != null) ? int.Equals(p1.Id, p2.Id) : false;
};

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 según el Id , otro usuario le dio la respuesta correcta que todo lo que necesita hacer es anular las implementaciones predeterminadas de GetHashCode() y Equals() en su clase de Person y luego simplemente usar la El método Distinct() caja de Linq para filtrar cualquier duplicado.


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

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

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

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


List<Person>lst=new List<Person>
        var result1 = lst.OrderByDescending(a => a.ID).Select(a =>new Player {ID=a.ID,Name=a.Name} ).Distinct();






distinct