C# 如何通过LINQ展平树?

C# 如何通过LINQ展平树?,c#,.net,linq,.net-4.0,tree,C#,.net,Linq,.net 4.0,Tree,所以我有一个简单的树: class MyNode { public MyNode Parent; public IEnumerable<MyNode> Elements; int group = 1; } 类MyNode { 公共MyNode父节点; 公共可数元素; int组=1; } 我有一个IEnumerable。我想将所有MyNode(包括内部节点对象(元素)的列表作为一个平面列表,其中组==1)。如何通过LINQ做这样的事情?你可以像这样压平一棵树: IEnumer

所以我有一个简单的树:

class MyNode
{
 public MyNode Parent;
 public IEnumerable<MyNode> Elements;
 int group = 1;
}
类MyNode
{
公共MyNode父节点;
公共可数元素;
int组=1;
}

我有一个
IEnumerable
。我想将所有
MyNode
(包括内部节点对象(
元素
)的列表作为一个平面列表
,其中
组==1
)。如何通过LINQ做这样的事情?

你可以像这样压平一棵树:

IEnumerable<MyNode> Flatten(IEnumerable<MyNode> e) =>
    e.SelectMany(c => Flatten(c.Elements)).Concat(new[] { e });
IEnumerable<MyNode> tree = ....
var res = tree.Flatten(node => node.Elements);
若要为“甚至更好的样式”赢得更多分数,请将
展平
转换为通用扩展方法,该方法采用树和从节点生成后代的函数:

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> e
,   Func<T,IEnumerable<T>> f
) => e.SelectMany(c => f(c).Flatten(f)).Concat(e);
公共静态IEnumerable展平(
这是一本数不清的书
,Func f
)=>e.SelectMany(c=>f(c).展平(f)).Concat(e);
按如下方式调用此函数:

IEnumerable<MyNode> Flatten(IEnumerable<MyNode> e) =>
    e.SelectMany(c => Flatten(c.Elements)).Concat(new[] { e });
IEnumerable<MyNode> tree = ....
var res = tree.Flatten(node => node.Elements);
IEnumerable树=。。。。
var res=tree.flatte(node=>node.Elements);
如果您更喜欢按预排序而不是按后排序进行展平,请切换
Concat(…)

void Main()
{
var allNodes=GetTreeNodes().flatte(x=>x.Elements);
allNodes.Dump();
}
公共静态类扩展方法
{
公共静态IEnumerable扁平化(此IEnumerable源,Func childrenSelector=null)
{
if(source==null)
{
返回新列表();
}
var列表=来源;
if(childrenSelector!=null)
{
foreach(源中的var项)
{
list=list.Concat(childrenSelector(item).Flatten(childrenSelector));
}
}
退货清单;
}
}
IEnumerable GetTreeNodes(){
返回新的[]{
new MyNode{Elements=new[]{new MyNode()}},
new MyNode{Elements=new[]{new MyNode(),new MyNode(),new MyNode()}
};
}
类MyNode
{
公共MyNode父节点;
公共可数元素;
int组=1;
}

接受答案的问题是,如果树很深,那么效率很低。如果这棵树很深,它就会把它吹倒。您可以使用显式堆栈解决此问题:

public static IEnumerable<MyNode> Traverse(this MyNode root)
{
    var stack = new Stack<MyNode>();
    stack.Push(root);
    while(stack.Count > 0)
    {
        var current = stack.Pop();
        yield return current;
        foreach(var child in current.Elements)
            stack.Push(child);
    }
}

为了完整起见,这里是dasblinkenlight和Eric Lippert的答案组合。单元测试和所有内容。:-)

公共静态IEnumerable展平(
这是数不清的项目,
Func(儿童)
{
var stack=新堆栈();
foreach(项目中的var项目)
堆栈。推送(项目);
而(stack.Count>0)
{
var current=stack.Pop();
产生回流电流;
var children=getChildren(当前);
如果(children==null)继续;
foreach(儿童中的儿童变量)
栈.推(子);
}
}

如果其他人发现了这一点,但在压平树后还需要知道其级别,这将扩展Konamiman的dasblinkenlight和Eric Lippert的解决方案组合:

    public static IEnumerable<Tuple<T, int>> FlattenWithLevel<T>(
            this IEnumerable<T> items,
            Func<T, IEnumerable<T>> getChilds)
    {
        var stack = new Stack<Tuple<T, int>>();
        foreach (var item in items)
            stack.Push(new Tuple<T, int>(item, 1));

        while (stack.Count > 0)
        {
            var current = stack.Pop();
            yield return current;
            foreach (var child in getChilds(current.Item1))
                stack.Push(new Tuple<T, int>(child, current.Item2 + 1));
        }
    }
公共静态IEnumerable级别(
这是数不清的项目,
Func getChilds)
{
var stack=新堆栈();
foreach(项目中的var项目)
stack.Push(新元组(item,1));
而(stack.Count>0)
{
var current=stack.Pop();
产生回流电流;
foreach(getChilds中的var child(current.Item1))
stack.Push(新元组(child,current.Item2+1));
}
}

更新:

适合对嵌套级别(深度)感兴趣的人。显式枚举器堆栈实现的优点之一是,在任何时刻(尤其是在生成元素时),
stack.Count
表示当前的处理深度。因此,考虑到这一点并利用C#7.0值元组,我们可以简单地更改方法声明,如下所示:

public static IEnumerable<(T Item, int Level)> ExpandWithLevel<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
然后我们可以通过在上面应用simple
Select
来实现原始方法:

public static IEnumerable<T> Expand<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector) =>
    source.ExpandWithLevel(elementSelector).Select(e => e.Item);
公共静态IEnumerable扩展(
此IEnumerable源,Func elementSelector)=>
ExpandWithLevel(elementSelector)。选择(e=>e.Item);
原件:

令人惊讶的是,没有人(甚至Eric)显示递归预阶DFT的“自然”迭代端口,因此它是:

    public static IEnumerable<T> Expand<T>(
        this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
    {
        var stack = new Stack<IEnumerator<T>>();
        var e = source.GetEnumerator();
        try
        {
            while (true)
            {
                while (e.MoveNext())
                {
                    var item = e.Current;
                    yield return item;
                    var elements = elementSelector(item);
                    if (elements == null) continue;
                    stack.Push(e);
                    e = elements.GetEnumerator();
                }
                if (stack.Count == 0) break;
                e.Dispose();
                e = stack.Pop();
            }
        }
        finally
        {
            e.Dispose();
            while (stack.Count != 0) stack.Pop().Dispose();
        }
    }
公共静态IEnumerable扩展(
此IEnumerable源(Func elementSelector)
{
var stack=新堆栈();
var e=source.GetEnumerator();
尝试
{
while(true)
{
while(如MoveNext())
{
var项目=e.当前;
收益回报项目;
变量元素=元素选择器(项目);
如果(elements==null)继续;
栈推(e);
e=elements.GetEnumerator();
}
如果(stack.Count==0)中断;
e、 处置();
e=stack.Pop();
}
}
最后
{
e、 处置();
而(stack.Count!=0)stack.Pop().Dispose();
}
}

结合Dave和Ivan Stoev的答案,以防需要嵌套级别,列表“按顺序”展平,而不是像Konamiman给出的答案那样反转

 public static class HierarchicalEnumerableUtils
    {
        private static IEnumerable<Tuple<T, int>> ToLeveled<T>(this IEnumerable<T> source, int level)
        {
            if (source == null)
            {
                return null;
            }
            else
            {
                return source.Select(item => new Tuple<T, int>(item, level));
            }
        }

        public static IEnumerable<Tuple<T, int>> FlattenWithLevel<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
        {
            var stack = new Stack<IEnumerator<Tuple<T, int>>>();
            var leveledSource = source.ToLeveled(0);
            var e = leveledSource.GetEnumerator();
            try
            {
                while (true)
                {
                    while (e.MoveNext())
                    {
                        var item = e.Current;
                        yield return item;
                        var elements = elementSelector(item.Item1).ToLeveled(item.Item2 + 1);
                        if (elements == null) continue;
                        stack.Push(e);
                        e = elements.GetEnumerator();
                    }
                    if (stack.Count == 0) break;
                    e.Dispose();
                    e = stack.Pop();
                }
            }
            finally
            {
                e.Dispose();
                while (stack.Count != 0) stack.Pop().Dispose();
            }
        }
    }
公共静态类HierarchycalEnumerableUtils
{
私有静态IEnumerable ToLevel(此IEnumerable源,int级别)
{
if(source==null)
{
返回null;
}
其他的
{
返回source.Select(item=>newtuple(item,level));
}
}
公共静态IEnumerable级别(此IEnumerable源,Func elementSelector)
{
var stack=新堆栈();
var leveledSource=源。至水平(0);
var e=leveledSource.GetEnumerator();
尝试
{
while(true)
{
而(e.M
public static IEnumerable<T> Expand<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector) =>
    source.ExpandWithLevel(elementSelector).Select(e => e.Item);
    public static IEnumerable<T> Expand<T>(
        this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
    {
        var stack = new Stack<IEnumerator<T>>();
        var e = source.GetEnumerator();
        try
        {
            while (true)
            {
                while (e.MoveNext())
                {
                    var item = e.Current;
                    yield return item;
                    var elements = elementSelector(item);
                    if (elements == null) continue;
                    stack.Push(e);
                    e = elements.GetEnumerator();
                }
                if (stack.Count == 0) break;
                e.Dispose();
                e = stack.Pop();
            }
        }
        finally
        {
            e.Dispose();
            while (stack.Count != 0) stack.Pop().Dispose();
        }
    }
 public static class HierarchicalEnumerableUtils
    {
        private static IEnumerable<Tuple<T, int>> ToLeveled<T>(this IEnumerable<T> source, int level)
        {
            if (source == null)
            {
                return null;
            }
            else
            {
                return source.Select(item => new Tuple<T, int>(item, level));
            }
        }

        public static IEnumerable<Tuple<T, int>> FlattenWithLevel<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
        {
            var stack = new Stack<IEnumerator<Tuple<T, int>>>();
            var leveledSource = source.ToLeveled(0);
            var e = leveledSource.GetEnumerator();
            try
            {
                while (true)
                {
                    while (e.MoveNext())
                    {
                        var item = e.Current;
                        yield return item;
                        var elements = elementSelector(item.Item1).ToLeveled(item.Item2 + 1);
                        if (elements == null) continue;
                        stack.Push(e);
                        e = elements.GetEnumerator();
                    }
                    if (stack.Count == 0) break;
                    e.Dispose();
                    e = stack.Pop();
                }
            }
            finally
            {
                e.Dispose();
                while (stack.Count != 0) stack.Pop().Dispose();
            }
        }
    }
public static IEnumerable<T> TraverseAndFlatten<T, V>(this IEnumerable<T> items, Func<T, IEnumerable<T>> nested, Func<T, V> orderBy)
{
    var stack = new Stack<T>();
    foreach (var item in items.OrderBy(orderBy))
        stack.Push(item);

    while (stack.Count > 0)
    {
        var current = stack.Pop();
        yield return current;

        var children = nested(current).OrderBy(orderBy);
        if (children == null) continue;

        foreach (var child in children)
            stack.Push(child);
    }
}
var flattened = doc.TraverseAndFlatten(x => x.DependentDocuments, y => y.Document.DocDated).ToList();
Item_0--Item_00
        Item_01

Item_1--Item_10
        Item_11
        Item_12--Item_120
public static IEnumerable<(T, int[])> Expand<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getChildren) {
    var stack = new Stack<IEnumerator<T>>();
    var e = source.GetEnumerator();
    List<int> indexes = new List<int>() { -1 };
    try {
        while (true) {
            while (e.MoveNext()) {
                var item = e.Current;
                indexes[stack.Count]++;
                yield return (item, indexes.Take(stack.Count + 1).ToArray());
                var elements = getChildren(item);
                if (elements == null) continue;
                stack.Push(e);
                e = elements.GetEnumerator();
                if (indexes.Count == stack.Count)
                    indexes.Add(-1);
                }
            if (stack.Count == 0) break;
            e.Dispose();
            indexes[stack.Count] = -1;
            e = stack.Pop();
        }
    } finally {
        e.Dispose();
        while (stack.Count != 0) stack.Pop().Dispose();
    }
}
public static class IEnumerableExtensions
{
    public static IEnumerable<T> Flatten<T>(
        this IEnumerable<T> items, 
        Func<T, IEnumerable<T>> getChildren)
    {
        if (items == null)
            yield break;

        var stack = new Stack<T>(items);
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            yield return current;

            if (current == null) continue;

            var children = getChildren(current);
            if (children == null) continue;

            foreach (var child in children)
                stack.Push(child);
        }
    }
}
[TestClass]
public class IEnumerableExtensionsTests
{
    [TestMethod]
    public void NullList()
    {
        IEnumerable<Test> items = null;
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(0, flattened.Count());
    }
    [TestMethod]
    public void EmptyList()
    {
        var items = new Test[0];
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(0, flattened.Count());
    }
    [TestMethod]
    public void OneItem()
    {
        var items = new[] { new Test() };
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(1, flattened.Count());
    }
    [TestMethod]
    public void OneItemWithChild()
    {
        var items = new[] { new Test { Id = 1, Children = new[] { new Test { Id = 2 } } } };
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(2, flattened.Count());
        Assert.IsTrue(flattened.Any(i => i.Id == 1));
        Assert.IsTrue(flattened.Any(i => i.Id == 2));
    }
    [TestMethod]
    public void OneItemWithNullChild()
    {
        var items = new[] { new Test { Id = 1, Children = new Test[] { null } } };
        var flattened = items.Flatten(i => i.Children);
        Assert.AreEqual(2, flattened.Count());
        Assert.IsTrue(flattened.Any(i => i.Id == 1));
        Assert.IsTrue(flattened.Any(i => i == null));
    }
    class Test
    {
        public int Id { get; set; }
        public IEnumerable<Test> Children { get; set; }
    }
}
var flatten = topNode.GetAllNodes();
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> items, 
    Func<T,IEnumerable<T>> getChildren)
    {
        if (items == null)
            yield break;

        var queue = new Queue<T>();

        foreach (var item in items) {
            if (item == null)
                continue;

            queue.Enqueue(item);

            while (queue.Count > 0) {
                var current = queue.Dequeue();
                yield return current;

                if (current == null)
                    continue;

                var children = getChildren(current);
                if (children == null)
                    continue;

                foreach (var child in children)
                    queue.Enqueue(child);
            }
        }

    }
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source,
    Func<T, IEnumerable<T>> childrenSelector)
{
    var queue = new Queue<T>(source);
    while (queue.Count > 0)
    {
        var current = queue.Dequeue();
        yield return current;
        var children = childrenSelector(current);
        if (children == null) continue;
        foreach (var child in children) queue.Enqueue(child);
    }
}