I've written a generic solution that is based on Teudimundo's solution. This is for object trees where the object contains a collection of itself. Here is a simple class to demonstrate.
public class Thing
{
public IEnumerable<Thing> Children { get; set; }
public bool IsLeaf => Children == null || !Children.Any();
}
So, with this very basic setup, you can see that it creates a tree of Thing
. I have Children
and a property, IsLeaf
, letting us know if this Thing
is a leaf. From this, I created an extension method for IEnumerable
that allows you to find all the leaves for this kind of scenario.
public static class EnumerableExtensions
{
public static IEnumerable<TSource> Leaves<TSource>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> children, Func<TSource, bool> isLeaf)
{
var nodes = new Stack<TSource>(source);
while (nodes.Any())
{
var current = nodes.Pop();
if(isLeaf(current))
{
yield return current;
continue;
}
foreach (var child in children(current))
{
if (isLeaf(child))
{
yield return child;
}
else
{
nodes.Push(child);
}
}
}
}
}
This is great because it works in Linq statements. So, if you have a collection of Thing
, you can easily traverse the tree to find all the leaves of your collection. Here is what calling this extension method looks like.
var leaves = things.Leaves(t => t.Children, t => t.IsLeaf);
things
is an IEnumerable
of Thing
. It is easiest to make a new List<Thing>()
with a bunch of Thing
in it, and assign it to things
. The parameters to Leaves
are the two properties we need to traverse the tree. The first is Children
which are the child nodes of the tree. The second is IsLeaf
which is the property that tells us if this Thing
is a leaf or not. This call returns an IEnumerable<Thing>
containing only leaves from things
. Use .ToList()
to make it to a list if need be. I don't think it can get much simpler than that!