I have an object hierarchy (MasterNode -> ChildNodes) where master and child nodes are of the same type, and there are only two levels (top level and children) like this (\'
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);
}
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);
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);
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
.