伪造 IGrouping 以供 LINQ 使用

3
假设您有一个大型数据集,该数据集可能会或可能不会被特定条件过滤,而这些条件的计算可能是密集的。在未经过滤的情况下,元素按该条件的值进行分组 - 该条件仅计算一次。
然而,在过滤已发生的情况下,尽管随后的代码仍希望看到一个 IEnumerable<IGrouping<TKey, TElement>> 集合,但执行 GroupBy 操作并导致每个元素的条件被重新评估第二次是没有意义的。相反,我想通过适当地包装过滤结果来创建一个 IEnumerable<IGrouping<TKey, TElement>> ,从而避免再次评估条件。
除了实现自己的提供 IGrouping 接口的类之外,是否还有其他方法可以实现此优化?是否有现有的 LINQ 方法支持此操作,以便给出 IEnumerable<IGrouping<TKey, TElement>> 结果?是否有其他我没有考虑过的方法?
3个回答

3

条件只计算一次

我希望那些键还存在某个地方...

如果您的数据结构类似于这样:

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)

我不能保证键值的稳定性-这是一个使用我没有编写过的结构的复杂查询,因此不能依赖于任何类型的缓存。然而,你启发了我一个可能有效的解决方案-现在看起来如此明显。 - Jeff Yates

2
David B's answer的启发,我想出了一个简单的解决方案。如此简单,以至于我不知道我是怎么错过它的。
为了执行过滤,我显然需要知道我正在过滤的条件的值。因此,给定一个条件c,我可以将过滤后的列表投影为:
filteredList.GroupBy(x => c)

这样可以避免对元素(用 x 表示)的任何属性进行重新计算。
我意识到的另一个解决方案是,在执行过滤之前反转查询的顺序并执行分组。这也意味着条件只会被评估一次,尽管会不必要地分配我随后不会使用的分组。

我应该补充说明的是,这意味着需要通过所有项目进行一次循环来对它们进行分组,如果可能的话,我仍然希望避免这种情况。我可能会创建自己的分组类,以便可以避免这种情况。 - Jeff Yates

0
把结果放进一个 LookUp 中,然后在接下来的时间里使用它怎么样?
var lookup = data.ToLookUp(i => Foo(i));

很遗憾,查找不实现IEnumerable<IGrouping<TKey, TElement>>。我想我可以将过滤和非过滤的组都放入查找中,但我希望避免对过滤列表进行额外处理,并避免对后续代码进行任何更改。我会研究一下并回复。 - Jeff Yates

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接