【问题标题】:Linq - convert an ILookup into another ILookupLinq - 将一个 ILookup 转换为另一个 ILookup
【发布时间】:2010-11-06 23:16:22
【问题描述】:

这应该很简单,但我想不出一个好的方法来做到这一点。如何将一个 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);
}

有人可以改进吗?

-- 布赖恩

【问题讨论】:

    标签: linq transform ilookup


    【解决方案1】:

    这个怎么样:

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

    【讨论】:

    • 啊,很好。这行得通,它肯定是对我的改进,但它具有相同的弱点 - 它为查找的每个元素实例化一个中间对象。有没有办法用纯枚举来做到这一点?
    • 是的 - LINQ 查询通常会实例化中间对象。不过,性能成本很少会成为大问题。
    • Joe:关于性能成本,您是对的。在大多数情况下,您需要在您的 Lookup 中包含数以百万计的项目,然后才能注意到中间对象的影响,因为它们的生命周期非常短暂。
    【解决方案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 中,它是一种值类型,存储在堆栈中,因此不需要任何中间内存分配。我分析了一个测试,它创建了一个带有 100 个键的 Lookup&lt;int,int&gt;,每个键有 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 秒

    【讨论】:

    • 不。那甚至不编译。问题是 x 是一个 IGrouping,所以你正在构建一个 ILookup>.
    • brianberns:你说得对,它需要SelectMany 步骤。请注意,我使用了KeyValuePair,它是一种值类型,因此它不会为Lookup 中的每个元素创建一个新对象。
    • 是的。你和 Joe A. 想出了(基本上)相同的答案。但是您认为有一种不需要实例化新对象的方法吗?
    • brianberns:您要避免实例化哪些对象?那里唯一的new 实例化了一个KeyValuePair,它应该留在堆栈上并且不会产生任何垃圾。
    • brianberns:我刚刚检查并确定我的方法只为每个键分配一个对象,超出了首先创建 Lookup 所需的数量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多