Linq - convertire un iLookup in un altro iLookup
Domanda
Questo dovrebbe essere semplice, ma non riesco a pensare ad un buon modo per farlo. Come si fa a trasformare un iLookup in un altro iLookup? Ad esempio, come è possibile copiare / clonare un iLookup, producendo un'altra iLookup con le stesse chiavi e gli stessi gruppi?
Ecco il mio tentativo zoppo:
static ILookup<TKey, TValue> Copy<TKey, TValue>(ILookup<TKey, TValue> lookup)
{
return lookup
.ToDictionary(
grouping => grouping.Key,
grouping => grouping.ToArray())
.SelectMany(pair =>
pair
.Value
.Select(value =>
new KeyValuePair<TKey, TValue>(pair.Key, value)))
.ToLookup(pair => pair.Key, pair => pair.Value);
}
Qualcuno può migliorare questa?
- Brian
Soluzione
Questo fare ciò che si vuole?
static ILookup<TKey, TValue> Copy<TKey, TValue>(ILookup<TKey, TValue> lookup)
{
return lookup.
SelectMany(g => g,
(g, v) => new KeyValuePair<TKey, TValue>(g.Key, v)).
ToLookup(kvp => kvp.Key, kvp => kvp.Value);
}
Naturalmente, se si vuole trasformare i valori in qualche modo, forse si desidera qualcosa di simile:
static ILookup<TKey, TValueOut> Transform<TKey, TValue, TValueOut>(
ILookup<TKey, TValue> lookup,
Func<TValue, TValueOut> selector)
{
return lookup.
SelectMany(g => g,
(g, v) => new KeyValuePair<TKey, TValueOut>(g.Key, selector(v))).
ToLookup(kvp => kvp.Key, kvp => kvp.Value);
}
Si noti che questo metodo contiene i valori intermedi in un KeyValuePair
che, essendo un tipo di valore, memorizzato nello stack e pertanto non richiede alcuna allocazioni di memoria intermedie. Ho fatto il profilo di un test che crea un Lookup<int,int>
con 100 tasti, ciascuno con 10.000 voci (per un totale di 1.000.000).
- Creazione del
Lookup
fa 1610 allocazioni. - Copia con il mio metodo non 1712 allocazioni (tutti gli stanziamenti necessari per creare lo più uno per ciascun delegato nella chiamata
SelectMany
e uno per l'enumeratore per ogni tasto). - Copia con anonimi oggetti invece di
KeyValuePair
fa 1,001,712 allocazioni (tutti gli stanziamenti necessari per copiare più uno per ogni elemento).
CPU-saggio, anche con 100.000 elementi per chiave per le prestazioni Lookup
tra i due metodi di copiatura era identica. Con 1.000.000 di elementi per tasto, la prestazione è stata diversa tra i due metodi:
- 5.1 sec per creare
- 5.9 sec a copiare con
KeyValuePair
- 6.3 sec per copiare con anonimi oggetti
Altri suggerimenti
Che ne dite di questo:
return lookup
.SelectMany (grp => grp, (grp, item) => new { grp.Key, item})
.ToLookup (x => x.Key, x => x.item);