query linq per restituire valori di campo distinti da un elenco di oggetti


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

Supponiamo che ci sia un elenco con 100 elementi di obj, ma solo 10 typeID univoci.
È possibile scrivere una query LINQ che restituisca i 10 int univoci dall'elenco di oggetti?


Questa domanda è così facile e non va bene per la taglia.
Pensatore

Risposte:


157
objList.Select(o=>o.typeId).Distinct()

2
La seconda forma sembra utilizzare un sovraccarico di Distinct che non esiste per quanto ne so.
Jon Skeet

Oops, rimosso il secondo, grazie @Jon Skeet, che può essere fatto con IEqualityComparer
Arsen Mkrtchyan

3
Come menzionato da Jon Skeet, questo restituirà solo le proprietà specificate in Select.
Peet vd Westhuizen

59

Supponendo che si desideri l'oggetto completo, ma si desideri trattare solo con distinzione typeID, non c'è nulla di integrato in LINQ per semplificare questa operazione. (Se solo i typeIDvalori, è facile: proiettalo con Selecte quindi usa la Distinctchiamata normale .)

In MoreLINQ abbiamo ilDistinctBy operatore che potresti usare:

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

Questo però funziona solo per LINQ to Objects.

Puoi usare un raggruppamento o una ricerca, è solo un po 'fastidioso e inefficiente:

var distinct = list.GroupBy(x => x.typeID, (key, group) => group.First());

Per far apparire DistinctBy devi aggiungere lo spazio dei nomi Microsoft.Ajax.Utilities
Shiljo Paulson

1
@ Shil: No, stavo scrivendo di DistinctBy in MoreLINQ. Niente a che vedere con Microsoft.Ajax.Utilities.
Jon Skeet

Ora posso vedere che c'è un sovraccarico di Distinct in LINQ che accetta un IEqualityComparer come parametro e restituisce un elenco di oggetti distinti a seconda dell'implementazione dei metodi in IEqualityComparer
Dipendu Paul

1
@DipenduPaul: Sì, ma ciò significa comunque creare un comparatore di uguaglianza per una data proprietà, il che è fastidioso e rende più difficile la lettura. Se puoi accettare la dipendenza MoreLINQ, penso che sia più pulito.
Jon Skeet,

27

Se vuoi solo usare Linq puro, puoi usare groupby:

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

Se desideri che un metodo venga utilizzato in tutta l'app, in modo simile a quello che fa 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;
        }
    }
}

Utilizzando questo metodo per trovare i valori distinti utilizzando solo la proprietà Id, è possibile utilizzare:

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

puoi utilizzare più proprietà:

var query = objs.DistinctBy(p => new { p.TypeId, p.Name });

Grazie per la parte delle proprietà multiple !
Nae

7

Certo, usa Enumerable.Distinct.

Data una raccolta di obj(ad esempio foo), faresti qualcosa del genere:

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

5

Penso che questo sia quello che stai cercando:

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

Simile a questo Restituzione di un IQueryable distinto con LINQ?


.Firstva bene, dal momento che non avresti il ​​gruppo se non ci fosse qualcosa in esso.
mqp

1
Puoi usare un sovraccarico alternativo di GroupByper rendere anche questo più semplice - vedi la mia risposta per un esempio.
Jon Skeet

3

Se vuoi solo usare Linq, puoi sovrascrivere i metodi Equals e GetHashCode .

Classe di prodotto :

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

Metodo principale :

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

1

Volevo associare un dato particolare al menu a discesa e dovrebbe essere distinto. Ho fatto quanto segue:

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

Vedi se aiuta

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.