Linq - 将ILookup转换为另一个ILookup

时间:2010-11-06 23:16:22

标签: linq transform 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);
}

任何人都可以改进吗?

- Brian

2 个答案:

答案 0 :(得分:4)

这个怎么样:

return lookup
  .SelectMany (grp => grp, (grp, item) => new { grp.Key, item})
  .ToLookup (x => x.Key, x => x.item);

答案 1 :(得分:1)

这样做你想要的吗?

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中的中间值,作为值类型,它存储在堆栈中,因此不需要任何中间内存分配。我描述了一个测试,它创建了一个带有100个密钥的Lookup<int,int>,每个密钥有10,000个项目(总计1,000,000个)。

  • 创建Lookup会执行1610次分配。
  • 使用我的方法复制它会执行1712次分配(创建它所需的所有分配加上SelectMany调用中每个代理的一个分配,以及每个密钥的枚举器一个分配。)
  • 使用匿名对象而不是KeyValuePair复制它会进行1,001,712次分配(复制所需的所有分配加上每个项目的分配)。

在CPU方面,即使两个复制方法之间的Lookup性能中每个键有100,000个元素也是相同的。每个键有1,000,000个元素,两种方法的性能不同:

  • 创建
  • 5.1秒
  • 使用KeyValuePair
  • 复制5.9秒
  • 使用匿名对象复制6.3秒