我在这个stackoverflow中发现了一个很好的分层分组解决方案。
How can I hierarchically group data using 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;
}
}
提前致谢
答案 0 :(得分:1)
有点挑战,但是你走了:
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所需的必要信息。
答案 1 :(得分:1)
主要问题应该是为什么要删除实现中的递归?您提供的代码具有最大递归深度= groupSelectors.Length。在这种情况下,我不认为堆栈使用应该是您的关注。
Ivan提供的解决方案是正确的,但我认为它也有间接递归方法,并提供相同的堆栈消耗级别。它不是命名声明的方法调用,而是构建嵌套委托(Func)调用链。
如果你的目标是欺骗一些静态代码分析工具(他们通常不喜欢递归调用),你可以将GroupByMany的一部分提取到单独的方法中并从另一个方法中调用。