Linq - конвертировать ILookup в другой ILookup
Это должно быть просто, но я не могу придумать хороший способ сделать это. Как вы преобразуете ILookup в другой ILookup? Например, как бы вы скопировали / клонировали ILookup, создав другой ILookup с теми же ключами и теми же группами?
Вот моя неудачная попытка:
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);
}
Кто-нибудь может улучшить это?
- Брайан
2 ответа
Делает ли это то, что вы хотите?
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);
}
Конечно, если вы хотите как-то преобразовать значения, возможно, вы хотите что-то вроде этого:
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);
}
Обратите внимание, что этот метод содержит промежуточные значения в KeyValuePair
который, будучи типом значения, хранится в стеке и, следовательно, не требует промежуточных выделений памяти. Я профилировал тест, который создает Lookup<int,int>
с 100 ключами, каждый из которых имеет 10 000 предметов (всего 1 000 000).
- Создание
Lookup
делает 1610 отведений. - Копирование его с помощью моего метода делает 1712 выделений (все выделения, необходимые для его создания, плюс одно для каждого делегата в
SelectMany
вызов и один для перечислителя для каждого ключа). - Копирование его с анонимными объектами вместо
KeyValuePair
выполняет 1 001 712 выделений (все выделения, необходимые для копирования, плюс по одному для каждого элемента).
CPU, даже с 100 000 элементов на ключ в Lookup
производительность между двумя методами копирования была одинаковой. С 1 000 000 элементов на ключ производительность была разной для двух методов:
- 5,1 сек для создания
- 5,9 сек для копирования
KeyValuePair
- 6,3 сек для копирования с анонимными объектами
Как насчет этого:
return lookup
.SelectMany (grp => grp, (grp, item) => new { grp.Key, item})
.ToLookup (x => x.Key, x => x.item);