ILookup vs. IGrouping
Domanda
Ho avuto difficoltà a articolare le differenze tra ILookup<TKey, TVal>
e IGrouping<TKey, TVal>
, e sono curioso di sapere se ho capito bene ora. LINQ aggravato il problema con la produzione di sequenze di elementi IGrouping
mentre mi dà anche un metodo di estensione ToLookup
. Così sembrava fossero la stessa fino a quando ho guardato più da vicino.
var q1 =
from n in N
group n by n.MyKey into g
select g;
// q1 is IEnumerable<IGrouping<TKey, TVal>>
che è equivalente a:
var q2 = N.GroupBy(n => n.MyKey, n => n);
// q2 is IEnumerable<IGrouping<TKey, TVal>>
Il che assomiglia molto:
var q3 = N.ToLookup(n => n.MyKey, n => n);
// q3 is ILookup<TKey, TVal>
Sono corretto nei seguenti analogie?
- Un
IGrouping<TKey, TVal>
è un singolo gruppo (cioè una sequenza a chiave), analogo alKeyValuePair<TKey, TVal>
cui il valore è in realtà una sequenza di elementi (anziché un singolo elemento) - Un
IEnumerable<IGrouping<TKey, TVal>>
è una sequenza di coloro (simile a quello che si ottiene quando l'iterazione di unIDictionary<TKey, TVal>
- Un
ILookup<TKey, TVal>
è più come unIDictionary<TKey, TVal>
se il valore è in realtà una sequenza di elementi
Soluzione
Sì, tutti questi sono corretti.
E ILookup<TKey, TValue>
estende anche IEnumerable<IGrouping<TKey, TValue>>
in modo da poter iterare su tutte le coppie chiave / raccolta, nonché (o al posto di) solo guardando le chiavi particolari.
Io fondamentalmente penso ILookup<TKey,TValue>
come essendo come IDictionary<TKey, IEnumerable<TValue>>
.
Tenete a mente che ToLookup
è un "farlo ora" operazione (esecuzione immediata), mentre un GroupBy
è differita. Come spesso accade, con il modo in cui "tirare LINQ" funziona, quando si inizia a tirare IGrouping
s dal risultato di un GroupBy
, deve leggere tutti i dati in ogni caso (perché non è possibile passare a metà gruppo attraverso), mentre in altre implementazioni esso può essere in grado di produrre un risultato streaming. (Non Push LINQ;. Mi aspetterei LINQ to Events per essere lo stesso)
Altri suggerimenti
C'è un'altra differenza importante tra iLookup e IDictionary: il primo fa rispettare immutabilità nel senso che qui esistono metodi per modificare i dati (tranne quando il consumatore effettua un cast esplicito). Al contrario, IDictionary ha metodi come "Aggiungi", che consentono di modificare i dati. Così, dal punto di vista funzionale programmazione e / o programmazione parallela, iLookup è migliore. (Solo Vorrei che ci fosse anche una versione di iLookup che assegna un solo valore a una chiave piuttosto che un gruppo.)
(. A proposito, sembra pena sottolineare che il rapporto tra IEnumerable e IList è in qualche modo simile a quello tra iLookup e IDictionary -. Il primo è immutabile, quest'ultimo non è)
GroupBy
e ToLookUp
ha quasi stessa funzionalità TRANNE in questo modo: Riferimento
GroupBy: L'operatore GroupBy ritorna gruppi di elementi basati su alcuni valore della chiave. Ogni gruppo è rappresentato da IGrouping oggetto.
ToLookup: ToLookup è lo stesso di GroupBy; l'unica differenza è l'esecuzione di GroupBy è differito che è esecuzione ToLookup immediato.
Consente chiara la differenza utilizzando il codice di esempio. supponiamo di avere una classe che rappresenta il modello Person
:
class Personnel
{
public int Id { get; set; }
public string FullName { get; set; }
public int Level { get; set; }
}
dopo che noi definiamo una lista di personnels
come di seguito:
var personnels = new List<Personnel>
{
new Personnel { Id = 1, FullName = "P1", Level = 1 },
new Personnel { Id = 2, FullName = "P2", Level = 2 },
new Personnel { Id = 3, FullName = "P3", Level = 1 },
new Personnel { Id = 4, FullName = "P4", Level = 1 },
new Personnel { Id = 5, FullName = "P5", Level =2 },
new Personnel { Id = 6, FullName = "P6", Level = 2 },
new Personnel { Id = 7, FullName = "P7", Level = 2 }
};
Ora ho bisogno di ottenere il personnels
raggruppati per loro livello. Ho due approccio qui. utilizzando GroupBy
o ToLookUp
. Se io uso GroupBy
, come detto prima, userà esecuzione differita, questo significa, che quando si scorrere l'insieme l'elemento successivo può o non può essere calcolato fino a quando non è richiesto.
var groups = personnels.GroupBy(p => p.Level);
personnels.RemoveAll(p => p.Level == 1);
foreach (var product in groups)
{
Console.WriteLine(product.Key);
foreach (var item in product)
Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
}
Nel codice sopra, innanzitutto raggruppato il personnels
, ma prima iterazione, ho rimosso alcuni personnels
. Come GroupBy
usa esecuzione differita, in modo che il risultato finale non comprenderà elementi rimossi, perché raggruppamento sarà calcolando nel punto foreach
qui.
Output:
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2
Ma se riscrivo il codice di cui sopra, come di seguito: (si noti che il codice è uguale al codice precedente, ad eccezione GroupBy
è sostituito dal ToLookUp
)
var groups = personnels.ToLookup(p => p.Level);
personnels.RemoveAll(p => p.Level == 1);
foreach (var product in groups)
{
Console.WriteLine(product.Key);
foreach (var item in product)
Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
}
Come ToLookUp
usa esecuzione immediata, ciò significa che quando chiamo il metodo ToLookUp
, risultato viene generato e applicato gruppo, quindi se rimuovere qualsiasi elemento da personnels
prima iterazione, che effetto solito il risultato finale.
Output:
1
1 >>> P1 >>> 1
3 >>> P3 >>> 1
4 >>> P4 >>> 1
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2
Nota:. GroupBy
e ToLookUp
sia restituire tipi diversi troppo
Si potrebbe utilizzare ToDictionary invece di ToLookup, ma è necessario prestare attenzione a questo: ( riferimento )
L'utilizzo di ToLookup () è molto simile a quella di ToDictionary (), entrambi consentono di specificare selettori a chiave, selettori di valore, e comparatori. La differenza principale è che ToLookup () permette (e si aspetta) le chiavi duplicate mentre ToDictionary () non lo fa