2009-07-29 15 views
101

In ASP.NET C# ho una struct:selezionare più campi da List in Linq

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_id e category_name, eseguire un DISTINCT e infine uno ORDERBY su category_name.

Ecco quello che ho adesso:

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 quale struttura dati verrà memorizzata in (non a string[])?

EDIT

Usando una lista di struct non è scolpito nella pietra. Se sarebbe opportuno cambiare la struttura dei dati di supporto per rendere più facile la selezione (ne scriverò molti), prenderei volentieri delle raccomandazioni.

+8

Mentre è non correlato al lato LINQ, vorrei * fortemente * consigliare di non usare strutture mutevoli o campi pubblici. Personalmente raramente creo le strutture in primo luogo, ma le strutture mutabili richiedono solo problemi. –

+0

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

+1

@Jon Skeet Perché? – Midhat

risposta

175

tipi anonimi consentono di selezionare i campi arbitrari in strutture di dati che sono fortemente digitati più avanti nel tuo codice:

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

Dal momento che (apparentemente) è necessario memorizzarlo per r 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(); 
+0

Voglio usare distinto su 1 colonna e recuperare più colonne.SO come posso farlo? –

+1

Non ho mai pensato di selezionare in un nuovo tipo. Nel mio caso ho selezionato un nuovo 'KeyValuePair'. – cjbarth

20

Si potrebbe utilizzare un tipo anonimo:

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

Il compilatore genera il codice per una classe con name e category_name proprietà e restituisce istanze di quella classe. È anche possibile specificare manualmente i nomi delle proprietà:

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

È possibile avere un numero arbitrario di proprietà.

3
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.

5

Questo è il compito per cui anonymous types sono molto adatti. È possibile restituire oggetti di un tipo che viene creato automaticamente dal compilatore, dedotto dall'utilizzo.

La sintassi è di questa forma:

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

Per il vostro caso, provare 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(); 
22
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); 

Edit: Reso più LINQ-ey!

+0

Haha, grazie, lo apprezzo molto. Apparentemente questa non era la domanda più difficile del mondo. – Chet

+0

@IRBMe Scusami, piccola domanda. Cosa è esattamente "valore"? –

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

È possibile selezionare più campi utilizzando linq Selezionare come illustrato in precedenza in vari esempi questo verrà restituito 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 risolve il problema

2

si può rendere un KeyValuePair, in modo che restituirà un "IEnumerable<KeyValuePair<string, string>>"

Quindi, sarà simile a questo:

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