You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

针对嵌套IGrouping的类SelectMany扩展方法实现方案

问题描述

对一个 enumerable 连续调用两次.GroupBy()后,会得到嵌套的IGrouping集合:

IEnumerable<IGrouping<TOuterKey, IGrouping<TInnerKey, TElement>>> source = enumerable.GroupBy(o => o.InnerKey).GroupBy(g => g.OuterKey);

普通嵌套IEnumerable只需调用.SelectMany()就能扁平化,但IGrouping的扁平化实现起来没那么简单。我想编写如下通用扩展方法,请问该如何实现?

public static IEnumerable<IGrouping<TResultKey, TResultElement>> SelectMany<TOuterKey, TInnerKey, TInputElement, TResultKey, TResultElement>(
    this IEnumerable<IGrouping<TOuterKey, IGrouping<TInnerKey, TInputElement>>> source,
    Func<IGrouping<TOuterKey, IGrouping<TInnerKey, TInputElement>>, TResultKey> keySelector,
    Func<TOuterKey, TInnerKey, TInputElement, TResultElement> valueSelector)
实现方案

要实现这个扩展方法,核心难点在于IGrouping<TKey, TElement>是接口,.NET框架内部的分组实现类无法直接实例化,因此我们需要先自定义一个IGrouping的实现类,再完成扩展方法的逻辑:

1. 自定义IGrouping实现类

internal class Grouping<TKey, TElement> : IGrouping<TKey, TElement>
{
    public TKey Key { get; }
    private readonly IEnumerable<TElement> _elements;

    public Grouping(TKey key, IEnumerable<TElement> elements)
    {
        Key = key;
        _elements = elements;
    }

    public IEnumerator<TElement> GetEnumerator() => _elements.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

2. 实现扩展方法

public static IEnumerable<IGrouping<TResultKey, TResultElement>> SelectMany<TOuterKey, TInnerKey, TInputElement, TResultKey, TResultElement>(
    this IEnumerable<IGrouping<TOuterKey, IGrouping<TInnerKey, TInputElement>>> source,
    Func<IGrouping<TOuterKey, IGrouping<TInnerKey, TInputElement>>, TResultKey> keySelector,
    Func<TOuterKey, TInnerKey, TInputElement, TResultElement> valueSelector)
{
    // 展开嵌套分组,提取所有元素及其关联的外层键、内层键,同时计算结果键
    var flattenedItems = source.SelectMany(outerGroup =>
        outerGroup.SelectMany(innerGroup =>
            innerGroup.Select(element => new
            {
                OuterKey = outerGroup.Key,
                InnerKey = innerGroup.Key,
                InputElement = element,
                ResultKey = keySelector(outerGroup)
            })));

    // 按结果键重新分组,并用自定义Grouping类包装返回
    return flattenedItems
        .GroupBy(item => item.ResultKey, item => valueSelector(item.OuterKey, item.InnerKey, item.InputElement))
        .Select(group => new Grouping<TResultKey, TResultElement>(group.Key, group));
}

逻辑说明

  1. 展开嵌套分组:通过两层SelectMany把外层分组、内层分组完全展开,同时为每个元素关联外层键、内层键,以及通过keySelector计算出的结果键。
  2. 重新分组与包装:用展开后的元素按结果键分组,再将每个分组转换为自定义的Grouping实例,最终返回符合要求的IEnumerable<IGrouping<TResultKey, TResultElement>>。

内容的提问来源于stack exchange,提问作者Philip Atz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 04:02:05