使用删除递归对数据进行分层分组

本文关键字:分层 数据 删除 递归 | 更新日期: 2023-09-27 18:36:13

我在这个堆栈溢出中找到了分层分组的好解决方案。

如何使用 LINQ 对数据进行分层分组?

它对我帮助很大,但我想问一下我如何才能达到相同的结果,但没有递归。老实说,我在转换它时遇到了问题,因为递归对我来说是自然的方法。任何人都可以将此方法转换为不使用递归?

用法:

var result = customers.GroupByMany(c => c.Country, c => c.City);

编辑:

public class GroupResult
{
    public object Key { get; set; }
    public int Count { get; set; }
    public IEnumerable<GroupResult> SubGroups { get; set; }
    public override string ToString()
    { return string.Format("{0} ({1})", Key, Count); }
}
public static class MyEnumerableExtensions
{
    public static IEnumerable<GroupResult> GroupByMany<TElement>(
        this IEnumerable<TElement> elements,
        params Func<TElement, object>[] groupSelectors)
    {
        if (groupSelectors.Length > 0)
        {
            var selector = groupSelectors.First();
            //reduce the list recursively until zero
            var nextSelectors = groupSelectors.Skip(1).ToArray();
            return
                elements.GroupBy(selector).Select(
                    g => new GroupResult
                    {
                        Key = g.Key,
                        Count = g.Count(),
                        SubGroups = g.GroupByMany(nextSelectors)
                    });
        }
        return null;
    }
}

提前致谢

使用删除递归对数据进行分层分组

有点挑战性,但你来了:

public class GroupResult<T>
{
    public object Key { get; set; }
    public int Count { get; set; }
    public IEnumerable<T> Items { get; set; }
    public IEnumerable<GroupResult<T>> SubGroups { get; set; }
    public override string ToString() { return string.Format("{0} ({1})", Key, Count); }
}
public static class MyEnumerableExtensions
{
    public static IEnumerable<GroupResult<TElement>> GroupByMany<TElement>(
        this IEnumerable<TElement> elements,
        params Func<TElement, object>[] groupSelectors)
    {
        Func<IEnumerable<TElement>, IEnumerable<GroupResult<TElement>>> groupBy = source => null;
        for (int i = groupSelectors.Length - 1; i >= 0; i--)
        {
            var keySelector = groupSelectors[i]; // Capture
            var subGroupsSelector = groupBy; // Capture
            groupBy = source => source.GroupBy(keySelector).Select(g => new GroupResult<TElement>
            {
                Key = g.Key,
                Count = g.Count(),
                Items = g,
                SubGroups = subGroupsSelector(g)
            });
        }
        return groupBy(elements);
    }
}

诀窍是以相反的顺序构建分组 lambda 表达式,使用上一步的结果并使用闭包来捕获执行 lambda 所需的必要信息。

主要问题应该是为什么要从实现中摆脱递归?您提供的代码具有最大递归深度 = groupSelectors.Length。在这种情况下,我认为堆栈使用不应该是您关心的问题。

Ivan 提供的解决方案是正确的,但我认为它也具有间接递归方法,并提供相同的堆栈消耗级别。它不是命名声明的方法调用,而是构建嵌套委托 (Func) 调用链。

如果您的目标是欺骗一些静态代码分析工具(他们通常不喜欢递归调用),则可以将 GroupByMany 的一部分提取到单独的方法中,然后从另一个方法调用一个。