Depth-first flattened collection of an object hierarchy using LINQ

蓝咒 提交于 2019-11-29 14:31:46

If you need more than two hierarchy levels you can use the following extension method which goes recursively through your object graph:

public static IEnumerable<T> Flat<T>(this IEnumerable<T> l, Func<T, IEnumerable<T>> f) =>
        l.SelectMany(i => new T[] { i }.Concat(f(i).Flat(f)));

It flattens the given IEnumerable<T> with the use of a function that maps a T to an IEnumerable<T> describing the parent -> children relation of your data.

The depth-first flattening is done by concatinating every element with its sub-tree and then joining them with SelectMany.

You can use it like this:

var flattened = Masternodes.Flat(c => c.children);

If you have a class like below

public class Node
{
    public string Name;
    public List<Node> Children = new List<Node>();
}

your linq would be

 Func<IEnumerable<Node>, IEnumerable<Node>> Flatten = null;
 Flatten = coll => coll.SelectMany(n=>n.Concat(Flatten(n.Children)));

Test Code:

Node[] roots = new Node[]{ new Node(){Name="A"},new Node(){Name="B"},new Node(){Name="C"} };
roots[0].Children.Add(new Node(){Name="D"});
roots[0].Children.Add(new Node(){Name="E"});
roots[0].Children.Add(new Node(){Name="F"});

roots[1].Children.Add(new Node(){Name="G"});

roots[2].Children.Add(new Node(){Name="H"});
roots[2].Children.Add(new Node(){Name="I"});

Func<IEnumerable<Node>, IEnumerable<Node>> Flatten = null;
Flatten = coll => coll.SelectMany(n=>n.Concat(Flatten(n.Children)));

var result = String.Join(",",Flatten(roots).Select(x=>x.Name));

Console.WriteLine(result);

Since you have only two levels, the following approach should work:

var result = (from parent in masternodes
              select new Node[] { parent }.Concat(parent.children)).SelectMany(i => i);

First, it creates enumerables of the parent plus its children:

[A, D, E, F]
[B, G]
[C, H]

and then it flattens them with SelectMany.

Say, we have the following classes:

public class MasterNode : ChildNode
{
    public List<ChildNode> ChildNodes;
}

public class ChildNode
{
    public string Value;
}

then

        List<MasterNode> list = new List<MasterNode>
        {
            new MasterNode
            {
                Value="A", 
                ChildNodes = new List<ChildNode>
                {
                    new ChildNode{Value = "D"},
                    new ChildNode{Value = "E"},
                    new ChildNode{Value = "F"}
                }
            },
            new MasterNode
            {
                Value="B", 
                ChildNodes = new List<ChildNode>
                {                        
                    new ChildNode{Value = "G"}
                }
            },
            new MasterNode
            {
                Value="C", 
                ChildNodes = new List<ChildNode>
                {
                    new ChildNode{Value = "H"},
                    new ChildNode{Value = "I"}
                }
            }
        };

        foreach (ChildNode c in list.SelectMany(l =>
                                {
                                   List<ChildNode> result = l.ChildNodes.ToList();
                                   result.Insert(0, l);
                                   return result;
                                }))
        {
            Console.WriteLine(c.Value);
        }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!