Selezionare più campi dall'elenco in Linq


128

In ASP.NET C # ho una struttura:

public struct Data
{
    public int item1;
    public int item2;
    public int category_id;
    public string category_name;
}

e ho un elenco di quelli. Voglio selezionare category_ide category_name, eseguendo un DISTINCTe, infine, un ORDERBYon category_name.

Ecco quello che ho ora:

List<Data> listObject = getData();
string[] catNames = listObject
                    .Select(i=> i.category_name)
                    .Distinct()
                    .OrderByDescending(s => s)
                    .ToArray();

Questo ovviamente ottiene solo il nome della categoria. La mia domanda è: come posso ottenere più campi e in quale struttura di dati lo memorizzerò (non a string[])?

MODIFICARE

L'uso di un elenco di strutture non è impostato su pietra. Se sarebbe consigliabile modificare la mia struttura di dati di supporto per rendere più facili le selezioni (scriverò molte di queste) allora accetterei volentieri consigli.


10
Sebbene non sia correlato al lato LINQ, ti consiglio vivamente di non utilizzare strutture mutabili o campi pubblici. Personalmente raramente creo strutture in primo luogo, ma strutture mutabili chiedono solo problemi.
Jon Skeet,

@Jon Skeet Grazie. Lo convertirò in una classe normale con membri privati.
Chet

1
@Midhat: le strutture mutabili causano tutti i tipi di problemi, poiché non si comportano come le persone si aspettano. E i campi pubblici danno una completa mancanza di incapsulamento.
Jon Skeet,

@Jon Skeet. Puoi essere più specifico con le insidie ​​delle strutture mutabili o indicarmi una lettura.
Midhat,

2
@Midhat: dai un'occhiata a stackoverflow.com/questions/441309/why-are-mutable-structs-evil per un punto di partenza.
Jon Skeet,

Risposte:


228

I tipi anonimi ti consentono di selezionare campi arbitrari in strutture di dati che vengono fortemente digitati in seguito nel tuo codice:

var cats = listObject
    .Select(i => new { i.category_id, i.category_name })
    .Distinct()
    .OrderByDescending(i => i.category_name)
    .ToArray();

Poiché (apparentemente) è necessario memorizzarlo per un uso successivo, è possibile utilizzare l'operatore GroupBy:

Data[] cats = listObject
    .GroupBy(i => new { i.category_id, i.category_name })
    .OrderByDescending(g => g.Key.category_name)
    .Select(g => g.First())
    .ToArray();

Voglio usare distinti su 1 colonna e recuperare più colonne. COSÌ come posso farlo?
Kishan Gajjar,

1
Non ho mai pensato a Selectun nuovo tipo. Nel mio caso ho selezionato un nuovo KeyValuePair.
cjbarth,

26
var selectedCategories =
    from value in
        (from data in listObject
        orderby data.category_name descending
        select new { ID = data.category_id, Name = data.category_name })
    group value by value.Name into g
    select g.First();

foreach (var category in selectedCategories) Console.WriteLine(category);

Modifica : reso più LINQ-ey!


Haha, grazie, beh, apprezzo l'aiuto. Apparentemente questa non era la domanda più difficile al mondo.
Chet,

@IRBMe Mi scusi, piccola domanda. Che cos'è esattamente il "valore"?
Fernando S. Kroes,

23

È possibile utilizzare un tipo anonimo:

.Select(i => new { i.name, i.category_name })

Il compilatore genererà il codice per una classe con namee category_nameproprietà e restituirà istanze di quella classe. Puoi anche specificare manualmente i nomi delle proprietà:

i => new { Id = i.category_id, Name = i.category_name }

Puoi avere un numero arbitrario di proprietà.


6

Puoi selezionare più campi usando linq Select come mostrato sopra in vari esempi, questo tornerà come Tipo Anonimo. Se vuoi evitare questo tipo anonimo ecco il trucco semplice.

var items = listObject.Select(f => new List<int>() { f.Item1, f.Item2 }).SelectMany(item => item).Distinct();

Penso che questo risolva il tuo problema


5

Questa è un'attività per cui tipi anonimi sono molto adatti. È possibile restituire oggetti di un tipo creato automaticamente dal compilatore, dedotto dall'uso.

La sintassi è di questa forma:

new { Property1 = value1, Property2 = value2, ... }

Per il tuo caso, prova qualcosa di simile al seguente:

var listObject = getData();
var catNames = listObject.Select(i =>
    new { CatName = i.category_name, Item1 = i.item1, Item2 = i.item2 })
    .Distinct().OrderByDescending(s => s).ToArray();

4
var result = listObject.Select( i => new{ i.category_name, i.category_id } )

Questo utilizza tipi anonimi, quindi è necessario la parola chiave var, poiché il tipo risultante dell'espressione non è noto in anticipo.


3
(from i in list
 select new { i.category_id, i.category_name })
 .Distinct()
 .OrderBy(i => i.category_name);

3

Puoi renderlo un KeyValuePair, quindi restituirà a "IEnumerable<KeyValuePair<string, string>>"

Quindi, sarà così:

.Select(i => new KeyValuePair<string, string>(i.category_id, i.category_name )).Distinct();

Ciò aggiunge inutili complessità senza un vantaggio
Matze

2
public class Student
{
    public string Name { set; get; }
    public int ID { set; get; }
}

class Program
{
  static void Main(string[] args)
    {
        Student[] students =
        {
        new Student { Name="zoyeb" , ID=1},
        new Student { Name="Siddiq" , ID=2},
        new Student { Name="sam" , ID=3},
        new Student { Name="james" , ID=4},
        new Student { Name="sonia" , ID=5}
        };

        var studentCollection = from s in students select new { s.ID , s.Name};

        foreach (var student in studentCollection)
        {
            Console.WriteLine(student.Name);
            Console.WriteLine(student.ID);
        }
    }
}
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.