【问题标题】:Faking IGrouping for LINQ为 LINQ 伪造 IGrouping
【发布时间】:2009-07-09 09:06:12
【问题描述】:

假设您有一个大型数据集,该数据集可能会或可能不会被数据集元素的特定条件过滤,这些条件可能需要大量计算。在未过滤的情况下,元素按该条件的值分组 - 条件计算一次。

然而,在过滤已经发生的情况下,虽然后续代码仍然期望看到一个IEnumerable<IGrouping<TKey, TElement>> 集合,但是执行一个GroupBy 操作会导致条件被重新设置是没有意义的- 对每个元素进行第二次评估。相反,我希望能够通过适当地包装过滤结果来创建IEnumerable<IGrouping<TKey, TElement>>,从而避免再次评估条件。

除了实现我自己的提供IGrouping 接口的类之外,还有其他方法可以实现这种优化吗?是否有现有的 LINQ 方法来支持这一点,这会给我IEnumerable<IGrouping<TKey, TElement>> 结果?还有其他我没有考虑过的方法吗?

【问题讨论】:

    标签: c# linq optimization


    【解决方案1】:

    条件计算一次

    我希望那些钥匙还在某处......

    如果你的数据是这样的结构:

    public class CustomGroup<T, U>
    {
      T Key {get;set;}
      IEnumerable<U> GroupMembers {get;set} 
    }
    

    您可以使用这样的查询来投影此类项目:

    var result = customGroups
      .SelectMany(cg => cg.GroupMembers, (cg, z) => new {Key = cg.Key, Value = z})
      .GroupBy(x => x.Key, x => x.Value)
    

    【讨论】:

    • 我不能保证保留键 - 这是一个复杂的查询,使用我没有编写的结构,因此不能依赖任何类型的缓存。但是,您启发了一个我认为可能可行的解决方案 - 现在看起来很明显。
    【解决方案2】:

    David B's answer 的启发,我想出了一个简单的解决方案。如此简单,以至于我不知道我是怎么错过的。

    为了执行过滤,我显然需要知道我过滤条件的值。因此,给定一个条件c,我可以将过滤后的列表投影为:

    filteredList.GroupBy(x => c)
    

    这避免了元素上的任何属性重新计算(由x 表示)。

    我意识到可行的另一个解决方案是在执行过滤之前反转查询的顺序并执行分组。这也意味着条件只被评估一次,尽管它会不必要地分配我随后不会使用的分组。

    【讨论】:

    • 我应该补充一点,这确实意味着循环遍历所有项目以对它们进行分组,如果可以的话,我仍然想避免这种情况。我仍然可以创建自己的分组类,这样我就可以避免这种情况。
    【解决方案3】:

    将结果放入LookUp 并在其余时间使用它怎么样?

    var lookup = data.ToLookUp(i => Foo(i));
    

    【讨论】:

    • 不幸的是,查找没有实现 IEnumerable>。我想我可以将过滤组和非过滤组都放在查找中,但我希望避免对过滤列表进行额外处理,并避免对后续代码进行任何更改。我会调查一下并回复。
    猜你喜欢
    • 2023-03-22
    • 2015-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多