2013-02-28 17 views
5

Ho utilizzato LINQ e Lambda Expressions per un po ', ma non mi sento ancora completamente a mio agio con ogni aspetto della funzione.Puoi spiegare questa funzione di raggruppamento lambda?

Quindi, mentre stavo lavorando a un progetto di recente, avevo bisogno di ottenere un elenco distinto di oggetti basati su alcune proprietà e ho trovato questo codice. Funziona, e ci sto bene, ma mi piacerebbe capire il meccanismo di raggruppamento. Non mi piace semplicemente collegare il codice e scappare dal problema se posso aiutarlo.

In ogni modo il codice è:

var listDistinct 
    =list.GroupBy(
     i => i.value1, 
     (key, group) => group.First() 
    ).ToList(); 

Nel codice di esempio di cui sopra, si sta chiamando prima GroupBy e passandolo un'espressione lambda dicendogli di gruppo dalla struttura value1. La seconda sezione del codice sta causando la confusione.

Capisco che key fa riferimento a value1 nella dichiarazione (key, group), ma non mi sto ancora muovendo per la testa tutto ciò che sta avvenendo.

risposta

7

Che cosa significa l'espressione

list.GroupBy(
    i => i.value1, 
    (key, group) => group.First()) 

fare?

Questo crea una query che, una volta eseguito, analizza la sequenza list per produrre una sequenza di gruppi, e poi progetti la sequenza dei gruppi in una nuova sequenza. In questo caso, la proiezione consiste nel togliere il primo elemento a ciascun gruppo.

Il primo lambda sceglie la "chiave" su cui sono costruiti i gruppi. In questo caso, tutti gli elementi nell'elenco che hanno la stessa proprietà value1 vengono inseriti in un gruppo. Il valore che condividono diventa la "chiave" del gruppo.

Il secondo progetto lambda dalla sequenza di gruppi con chiave; è come se avessi fatto un select sulla sequenza di gruppi. L'effetto netto di questa query è di scegliere un insieme di elementi dall'elenco in modo tale che ogni elemento della sequenza risultante abbia un valore diverso della proprietà value1.

La documentazione è qui:

http://msdn.microsoft.com/en-us/library/bb549393.aspx

Se la documentazione non è chiaro, sono felice di passare lungo le critiche al gestore documentazione.

Questo codice utilizza group come parametro formale di una lambda. group non è una parola chiave riservata?

No, group è una parola chiave contestuale. LINQ è stato aggiunto a C# 3.0, quindi potrebbe esistere già un programma esistente che utilizza group come identificativo. Questi programmi verrebbero interrotti quando ricompilati se group fosse stato creato come parola chiave riservata. Invece, group è una parola chiave solo nel contesto di un'espressione di query. Al di fuori di un'espressione di query è un identificatore ordinario.

Se si desidera richiamare l'attenzione sul fatto che si tratta di un identificatore ordinario o se si desidera utilizzare l'identificativo group all'interno di un'espressione di query, è possibile indicare al compilatore "considerare questo come identificativo, non come parola chiave" precedendolo con @. Se scrivevo il codice sopra, direi

list.GroupBy(
    i => i.value1, 
    (key, @group) => @group.First()) 

per chiarire.

Ci sono altre parole chiave contestuali in C#?

Sì. Li ho documentato qui:

http://ericlippert.com/2009/05/11/reserved-and-contextual-keywords/

+0

Grazie, Eric. La documentazione è abbastanza chiara. Capisco quindi che il "gruppo" usato nello scenario sopra non è una parola riservata e posso passare in un nome più descrittivo per quel lambda? Questo aiuta un bel po '- stavo inciampando pensando che quel gruppo fosse una parola riservata per questa operazione, ma sapere altrimenti lo rende un po' più ovvio per quanto sta accadendo. – elucid8

+0

Inoltre, sei un capo, signore. – elucid8

+0

@ elucid8: aggiornerò la mia risposta per rispondere alla tua seconda domanda. –

1
(key, group) => group.First() 

Sta prendendo semplicemente l'elemento First() all'interno di ciascun gruppo.

All'interno di tale espressione lambda key è una chiave che è stato utilizzato per creare quel gruppo (value1 nel tuo esempio) e group è IEnumerable<T> con tutti gli elementi che ha quel key.

2

utilizza this overload del metodo Enumerable.GroupBy:

public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector, 
    Func<TKey, IEnumerable<TSource>, TResult> resultSelector 
) 

che, come si legge sul MSDN:

Raggruppa gli elementi di una sequenza in base a una funzione selettore di chiave specificata e crea un valore di risultato da ciascun gruppo e la relativa chiave.

Quindi, a differenza di other overloads che restituiscono un gruppo di gruppi (cioè IEnumerable<IGrouping<TK, TS>>), questo sovraccarico consente di proiettare ogni gruppo a una singola istanza di un TResult di vostra scelta.

Nota che si potrebbe ottenere lo stesso risultato utilizzando la base GroupBy sovraccarico ed un Select:

var listDistinct = list 
    .GroupBy(i => i.value1) 
    .Select(g => g.First()) 
    .ToList(); 
4

Vorrei semplificare questo a un elenco di int e come farlo distinti in questo elenco utilizzando GroupBy :

var list = new[] {1, 2, 3, 1, 2, 2, 3}; 

se si chiama GroupBy con x => x, otterrete 3 gruppi con il tipo:

IEnumerable<IEnumerable<int>> 

{{1,1},{2,2,2},{3,3}} 

La chiave di ogni gruppo sono: 1, 2, 3. E poi, quando si chiama group.First(), vuol dire che si ottiene prima voce di ogni gruppo:

{1,1}: -> 1. 
{2,2,2}: -> 2 
{3,3} -> 3 

Così il risultato finale è: {1, 2, 3}

Il tuo caso è simile a questo.

0

Il sotto di sé che descrive esempio dovrebbe aiutare a capire il raggruppamento:

class Item 
{ 
    public int Value { get; set; } 
    public string Text { get; set; } 
} 

static class Program 
{ 
    static void Main() 
    { 
     // Create some items 
     var item1 = new Item {Value = 0, Text = "a"}; 
     var item2 = new Item {Value = 0, Text = "b"}; 
     var item3 = new Item {Value = 1, Text = "c"}; 
     var item4 = new Item {Value = 1, Text = "d"}; 
     var item5 = new Item {Value = 2, Text = "e"}; 

     // Add items to the list 
     var itemList = new List<Item>(new[] {item1, item2, item3, item4, item5}); 

     // Split items into groups by their Value 
     // Result contains three groups. 
     // Each group has a distinct groupedItems.Key --> {0, 1, 2} 
     // Each key contains a collection of remaining elements: {0 --> a, b} {1 --> c, d} {2 --> e} 
     var groupedItemsByValue = from item in itemList 
            group item by item.Value 
            into groupedItems 
            select groupedItems; 

     // Take first element from each group: {0 --> a} {1 --> c} {2 --> e} 
     var firstTextsOfEachGroup = from groupedItems in groupedItemsByValue 
            select groupedItems.First(); 

     // The final result 
     var distinctTexts = firstTextsOfEachGroup.ToList(); // Contains items where Text is: a, c, e 
    } 
} 
+0

Un bell'esempio, ma questa domanda riguarda il metodo lamda 'GroupBy' –

+0

@PhilippM: per chiarire l'idea ho deciso di modificare la sintassi illeggibile e concisa * di GroupBy * in una sintassi leggibile e leggibile delle query LINQ. Solo per chiarire le cose. –

0

E 'equvilant a

var listDistinct=(
    from i in list 
    group i by i.value1 into g 
    select g.First() 
    ).ToList(); 

La parte i => i.value1 nel codice originale è selettore a chiave. In questo codice, è semplicemente i.value nella sintassi degli elementi di gruppo tramite chiave.

La parte (key, group) => group.First() nel codice originale è un delegato del selettore dei risultati . In questo codice, viene scritto in una sintassi più semantica di da ... selezionare. è qui g è group nel codice originale.

Problemi correlati