consulta linq para devolver valores de campo distintos de una lista de objetos

89
class obj
{
    int typeId; //10 types  0-9 
    string uniqueString; //this is unique
}

Suponga que hay una lista con 100 elementos de obj, pero solo 10 typeID únicos.
¿Es posible escribir una consulta LINQ que devuelva las 10 entradas únicas de la lista de objs?

Patricio
fuente
Esta pregunta es tan fácil y no se ajusta a las recompensas.
Pensador

Respuestas:

155
objList.Select(o=>o.typeId).Distinct()
Arsen Mkrtchyan
fuente
2
La segunda forma parece usar una sobrecarga de Distinct que no existe hasta donde yo sé.
Jon Skeet
Vaya, se eliminó el segundo, gracias @Jon Skeet, que se puede hacer con IEqualityComparer
Arsen Mkrtchyan
3
Como mencionó Jon Skeet, esto solo devolverá las propiedades especificadas en Seleccionar.
Peet vd Westhuizen
58

Suponiendo que desea el objeto completo, pero solo desea lidiar con la distinción typeID, no hay nada integrado en LINQ para facilitar esto. (Si solo desea los typeIDvalores, es fácil: proyecte a eso Selecty luego use la Distinctllamada normal ).

En MoreLINQ tenemos el DistinctByoperador que puede utilizar:

var distinct = list.DistinctBy(x => x.typeID);

Sin embargo, esto solo funciona para LINQ to Objects.

Puede usar una agrupación o una búsqueda, es algo molesto e ineficiente:

var distinct = list.GroupBy(x => x.typeID, (key, group) => group.First());
Jon Skeet
fuente
Para que aparezca DistinctBy, debe agregar el espacio de nombres Microsoft.Ajax.Utilities
Shiljo Paulson
1
@Shil: No, estaba escribiendo sobre DistinctBy en MoreLINQ. Nada que ver con Microsoft.Ajax.Utilities.
Jon Skeet
Ahora puedo ver que hay una sobrecarga de Distinct en LINQ que toma un IEqualityComparer como parámetro y devuelve una lista de objetos distintos dependiendo de la implementación de métodos en IEqualityComparer
Dipendu Paul
1
@DipenduPaul: Sí, pero eso aún significa crear un comparador de igualdad para una propiedad determinada, lo cual es molesto y hace que sea más difícil de leer. Si puede tomar la dependencia MoreLINQ, creo que es más limpio.
Jon Skeet
22

Si solo desea usar Linq puro, puede usar groupby:

List<obj> distinct =
  objs.GroupBy(car => car.typeID).Select(g => g.First()).ToList();

Si desea que se utilice un método en toda la aplicación, similar a lo que hace MoreLinq :

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.Contains(keySelector(element)))
        {
            seenKeys.Add(keySelector(element));
            yield return element;
        }
    }
}

Usando este método para encontrar los valores distintos usando solo la propiedad Id, puede usar:

var query = objs.DistinctBy(p => p.TypeId);

puede utilizar varias propiedades:

var query = objs.DistinctBy(p => new { p.TypeId, p.Name });
JulioCT
fuente
¡Gracias por la parte de propiedades múltiples !
Nae
7

Claro, usa Enumerable.Distinct.

Dada una colección de obj(por ejemplo foo), harías algo como esto:

var distinctTypeIDs = foo.Select(x => x.typeID).Distinct();
Rosquilla
fuente
5

Creo que esto es lo que estás buscando:

    var objs= (from c in List_Objects 
orderby c.TypeID  select c).GroupBy(g=>g.TypeID).Select(x=>x.FirstOrDefault());      

Similar a esto ¿ Devolver un IQueryable distinto con LINQ?

Calibrar
fuente
.Firstestá bien, ya que no tendrías el grupo si no hubiera algo en él.
mqp
1
Puede usar una sobrecarga alternativa de GroupBypara simplificar esto también; vea mi respuesta para ver un ejemplo.
Jon Skeet
3

Si solo desea usar Linq, puede anular los métodos Equals y GetHashCode .

Clase de producto :

public class Product
{
    public string ProductName { get; set; }
    public int Id { get; set; }


    public override bool Equals(object obj)
    {
        if (!(obj is Product))
        {
            return false;
        }

        var other = (Product)obj;
        return Id == other.Id;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

Método principal :

static void Main(string[] args)
    {

        var products = new List<Product>
        {
            new Product{ ProductName="Product 1",Id = 1},
            new Product{ ProductName="Product 2",Id = 2},
            new Product{ ProductName="Product 4",Id = 5},
            new Product{ ProductName="Product 3",Id = 3},
            new Product{ ProductName="Product 4",Id = 4},
            new Product{ ProductName="Product 6",Id = 4},
            new Product{ ProductName="Product 6",Id = 4},
        };

        var itemsDistinctByProductName = products.Distinct().ToList();

        foreach (var product in itemsDistinctByProductName)
        {
            Console.WriteLine($"Product Id : {product.Id} ProductName : {product.ProductName} ");
        }

        Console.ReadKey();
    }
Reza Jenabi
fuente
1

Quería vincular un dato particular al menú desplegable y debería ser distinto. Hice lo siguiente:

List<ClassDetails> classDetails;
List<string> classDetailsData = classDetails.Select(dt => dt.Data).Distinct.ToList();
ddlData.DataSource = classDetailsData;
ddlData.Databind();

Mira si ayuda

Charmy Vora
fuente