113

So I have simple tree:

class MyNode
{
 public MyNode Parent;
 public IEnumerable<MyNode> Elements;
 int group = 1;
}

I have a IEnumerable<MyNode>. I want to get a list of all MyNode (including inner node objects (Elements)) as one flat list Where group == 1. How to do such thing via LINQ?

myWallJSON
  • 9,110
  • 22
  • 78
  • 149
  • 1
    What order do you want the flattened list to be in? – Philip Aug 06 '12 at 14:23
  • 1
    When do nodes stop having child nodes? I presume it's when `Elements` is null or empty? – Adam Houldsworth Aug 06 '12 at 14:24
  • might be duplicate with http://stackoverflow.com/questions/11827569/recursive-filtering-linq-to-objects – Tamir Aug 06 '12 at 14:26
  • The easiest / most clear way to address this is using a recursive LINQ query. This question: https://stackoverflow.com/questions/732281/expressing-recursion-in-linq has a lot of discussion over this, and [this](https://stackoverflow.com/a/793531/1550) particular answer goes in some detail as to how you'd implement it. – Alvaro Rodriguez Aug 06 '12 at 14:28

15 Answers15

170

You can flatten a tree like this:

IEnumerable<MyNode> Flatten(IEnumerable<MyNode> e) =>
    e.SelectMany(c => Flatten(c.Elements)).Concat(new[] { e });

You can then filter by group using Where(...).

To earn some "points for style", convert Flatten to an extension function in a static class.

public static IEnumerable<MyNode> Flatten(this IEnumerable<MyNode> e) =>
    e.SelectMany(c => c.Elements.Flatten()).Concat(e);

To earn more points for "even better style", convert Flatten to a generic extension method that takes a tree and a function that produces descendants from a node:

public static IEnumerable<T> Flatten<T>(
    this IEnumerable<T> e
,   Func<T,IEnumerable<T>> f
) => e.SelectMany(c => f(c).Flatten(f)).Concat(e);

Call this function like this:

IEnumerable<MyNode> tree = ....
var res = tree.Flatten(node => node.Elements);

If you would prefer flattening in pre-order rather than in post-order, switch around the sides of the Concat(...).

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • @AdamHouldsworth Thanks for the edit! The element in the call to `Concat` should be `new[] {e}`, not `new[] {c}` (it wouldn't even compile with `c` there). – Sergey Kalinichenko Aug 06 '12 at 15:05
  • 1
    I disagree: compiled, tested, and working with `c`. Using `e` doesn't compile. You can also add `if (e == null) return Enumerable.Empty();` to cope with null child lists. – Adam Houldsworth Aug 06 '12 at 15:08
  • Now that you've moved a bracket it'll compile :-) – Adam Houldsworth Aug 06 '12 at 15:11
  • @AdamHouldsworth Oh, you're right, my order of parentheses was wrong, too! – Sergey Kalinichenko Aug 06 '12 at 15:11
  • 2
    more like ` public static IEnumerable Flatten(this IEnumerable source, Func> f) { if (source == null) return Enumerable.Empty(); return source.SelectMany(c => f(c).Flatten(f)).Concat(source); }` – myWallJSON Aug 08 '12 at 13:55
  • 13
    Note that this solution is O(nh) where n is the number of items in the tree and h is the average depth of the tree. Since h can be between O(1) and O(n), this is between an O(n) and an O(n squared) algorithm. There are better algorithms. – Eric Lippert Dec 02 '13 at 18:33
  • 1
    I've noticed that the function will not add elements to the flattened list if the list is of IEnumerable. You can solve this by calling the function like this: var res = tree.Flatten(node => node.Elements.OfType) – Frank Horemans Jun 27 '18 at 07:53
  • @EricLippert this is true, but if performance is not a problem due to depth, this is one of the most readable solutions. – Victorio Berra Jan 30 '23 at 14:50
145

The problem with the accepted answer is that it is inefficient if the tree is deep. If the tree is very deep then it blows the stack. You can solve the problem by using an explicit stack:

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);
    }
}

Assuming n nodes in a tree of height h and a branching factor considerably less than n, this method is O(1) in stack space, O(h) in heap space and O(n) in time. The other algorithm given is O(h) in stack, O(1) in heap and O(nh) in time. If the branching factor is small compared to n then h is between O(lg n) and O(n), which illustrates that the naïve algorithm can use a dangerous amount of stack and a large amount of time if h is close to n.

Now that we have a traversal, your query is straightforward:

root.Traverse().Where(item=>item.group == 1);
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 3
    @johnnycardy: If you were going to argue a point then perhaps the code is not *obviously* correct. What could make it more clearly correct? – Eric Lippert Jan 21 '14 at 16:50
  • 1
    Firstly I didn't see how it went past the first layer; lack of recursion threw me off. Obviously that's the reason it's efficient. I just didn't read it properly. Secondly, `Traverse` is an extension on `MyNode` instead of `IEnumerable` - the OP doesn't have a root to call it with. Changing the solution to be a generic method on `IEnumerable` is kind of awkward. I did it by initialising the `Stack` with the enumerable to keep it simple, but that enumerates the whole top layer of the tree up front. – johnnycardy Jan 22 '14 at 10:31
  • How this function could be used with `IEnumerable` instead of `root`? – ebram khalil Jun 19 '14 at 09:39
  • @ebramtharwat: Well, what do you *usually* do with an `IEnumerable`? – Eric Lippert Jun 19 '14 at 13:42
  • Loop through it!. In my case I have tree in where I could have multiple roots .. So if I understand your code, then I 'll need to call `Traverse` on each root node I have, right? – ebram khalil Jun 19 '14 at 14:25
  • 4
    @ebramtharwat: Correct. You could call `Traverse` on all the elements. Or you could modify `Traverse` to take a sequence, and have it push all the elements of the sequence onto `stack`. Remember, `stack` is "elements I have not traversed yet". Or you could make a "dummy" root where your sequence is its children, and then traverse the dummy root. – Eric Lippert Jun 19 '14 at 16:05
  • 5
    If you do `foreach (var child in current.Elements.Reverse())` you will get a more expected flattening. In particular, children will appear in the order they appear rather than last child first. This shouldn't matter in most cases, but in my case I needed the flattening to be in a predictable and expected order. – Micah Zoltu Jul 07 '15 at 18:04
  • 3
    @MicahZoltu, you could avoid the `.Reverse` by exchanging the `Stack` for a `Queue` – Rubens Farias Sep 17 '15 at 08:18
  • 3
    @MicahZoltu You are correct about the order, but the problem with `Reverse` is that it creates additional iterators, which is what this approach is meant to avoid. @RubensFarias Substituting `Queue` for `Stack` results in breadth-first traversal. – Jack A. Nov 14 '17 at 23:41
  • Am I correct in assuming that this feature (non-quadratic recursive iterators) would be a valid replacement for your answer? @EricLippert (see https://github.com/dotnet/csharplang/issues/378) – Mafii Dec 01 '17 at 13:21
  • 1
    @Mafii: That's correct. The feature suggested there is in F# (as `yield!`, a most exciting feature) and was in a research version of C# called C-Omega, so we know it can be done. (Though I believe hearing that the version in C-Omega had correctness problems when it came to preserving the behaviour of exceptions; I don't recall the details.) – Eric Lippert Dec 01 '17 at 14:41
  • Why call this `Traverse` when the op was looking to `Flatten` a tree? – StingyJack Apr 19 '18 at 18:24
  • 1
    @StingyJack: I wished to emphasize that this is a lazy traversal; even better would be some indication of what kind of traversal it is. I really should have named it `DepthFirstTraversal`. – Eric Lippert Apr 19 '18 at 18:49
  • https://stackoverflow.com/a/73486312/659190, this for re-use. – Jodrell Aug 25 '22 at 11:00
31

Just for completeness, here is the combination of the answers from dasblinkenlight and Eric Lippert. Unit tested and everything. :-)

 public static IEnumerable<T> Flatten<T>(
        this IEnumerable<T> items,
        Func<T, IEnumerable<T>> getChildren)
 {
     var stack = new Stack<T>();
     foreach(var item in items)
         stack.Push(item);

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

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

         foreach (var child in children) 
            stack.Push(child);
     }
 }
Rubens Farias
  • 57,174
  • 8
  • 131
  • 162
Konamiman
  • 49,681
  • 17
  • 108
  • 138
  • 4
    To avoid NullReferenceException var children = getChildren(current); if (children != null) { foreach (var child in children) stack.Push(child); } – serg Apr 08 '15 at 03:36
  • 4
    I'd like to note that even though this does flatten the list, it returns it in the reverse order. The last element becomes first etc. – Corcus Nov 10 '16 at 17:11
  • Should also check and throw if `getChildren` is null. – Jodrell Aug 25 '22 at 10:52
24

Update:

For people interested in level of nesting (depth). One of the good things about explicit enumerator stack implementation is that at any moment (and in particular when yielding the element) the stack.Count represents the currently processing depth. So taking this into account and utilizing the C#7.0 value tuples, we can simply change the method declaration as follows:

public static IEnumerable<(T Item, int Level)> ExpandWithLevel<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)

and yield statement:

yield return (item, stack.Count);

Then we can implement the original method by applying simple Select on the above:

public static IEnumerable<T> Expand<T>(
    this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector) =>
    source.ExpandWithLevel(elementSelector).Select(e => e.Item);

Original:

Surprisingly no one (even Eric) showed the "natural" iterative port of a recursive pre-order DFT, so here it is:

    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();
        }
    }
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • I assume you switch `e` each time you call `elementSelector` to maintain pre-order - if order didn't matter, could you change the function to process all of each `e` once started? – NetMage Jan 26 '18 at 18:35
  • @NetMage I wanted specifically pre order. With small change it can handle post order. But the main point is, this is *Depth First Traversal*. For *Breath First Traversal* I would use `Queue`. Anyway, the idea here is to keep a small stack with enumerators, very similar to what is happening in the recursive implementation. – Ivan Stoev Jan 26 '18 at 18:39
  • @IvanStoev I was thinking the code would be simplified. I guess using the `Stack` would result in a zig-zag Breadth First Traversal. – NetMage Jan 26 '18 at 18:43
  • What is the point of maintaining a `Stack>` instead of a `Stack`? Enumerators are usually mutable values types, and are usually implemented as state machines. So I expect a `Stack>` solution to be generally memory inefficient, and to add pressure to the garbage collector (because of the boxed value types). – Theodor Zoulias Sep 19 '20 at 16:32
  • 2
    @TheodorZoulias (1) Imagine each level contains 1000 `T` items. Using `Stack` or `Queue` would require pushing/enqueuing 1000+1000+...+1000 items, while `Stack`> would have max Depth active enumerator objects. And enumerators in general are much lighter /use less memory than lists - basically some reference and position. What about boxing, only few BCL collection type enumerators are implemented with structs and only when you do direct `foreach`. Once you use LINQ or `IEnumerable`, all these optimizations are gone and same boxing/GC pressure applies. – Ivan Stoev Sep 19 '20 at 17:13
  • 1
    @TheodorZoulias (2) Using `Queue` is a must for Breath First traversal. Depth First traversal requires stack. The `Stack>` simply reflects the stack of the recursive implementation like `foreach (var e1 in source) foreach (var e2 in e1.Children) ... for each (var eN in eN-1.Children)` (I guess you know what `foreach` on `IEnumerable` is doing - `using (var en = GetEnumerator()) while en.MoveNext() { ... } ` etc.) – Ivan Stoev Sep 19 '20 at 17:27
  • 1
    Ivan after closer inspection you are right at both points. Boxing is unavoidable, and storing an enumerator of the children is certainly preferable than storing all the children. Upvoted. :-) – Theodor Zoulias Sep 19 '20 at 17:30
8

I found some small issues with the answers given here:

  • What if the initial list of items is null?
  • What if there is a null value in the list of children?

Built on the previous answers and came up with the following:

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);
        }
    }
}

And the unit tests:

[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; }
    }
}
Doug Clutter
  • 3,646
  • 2
  • 29
  • 31
6

Most of the answers presented here are producing depth-first or zig-zag sequences. For example starting with the tree below:

        1                   2 
       / \                 / \
      /   \               /   \
     /     \             /     \
    /       \           /       \
   11       12         21       22
  / \       / \       / \       / \
 /   \     /   \     /   \     /   \
111 112   121 122   211 212   221 222

Sergey Kalinichenko's answer produces this flattened sequence:

111, 112, 121, 122, 11, 12, 211, 212, 221, 222, 21, 22, 1, 2

Konamiman's answer (that generalizes Eric Lippert's answer) produces this flattened sequence:

2, 22, 222, 221, 21, 212, 211, 1, 12, 122, 121, 11, 112, 111

Ivan Stoev's answer produces this flattened sequence:

1, 11, 111, 112, 12, 121, 122, 2, 21, 211, 212, 22, 221, 222

If you are interested in a breadth-first sequence like this:

1, 2, 11, 12, 21, 22, 111, 112, 121, 122, 211, 212, 221, 222

...then this is the solution for you:

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);
    }
}

The difference in the implementation is basically using a Queue instead of a Stack. No actual sorting is taking place.


Caution: this implementation is far from optimal regarding memory-efficiency, since a large percentage of the total number of elements will end up being stored in the internal queue during the enumeration. Stack-based tree-traversals are much more efficient regarding memory usage than Queue-based implementations.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
4

In case anyone else finds this, but also needs to know the level after they've flattened the tree, this expands on Konamiman's combination of dasblinkenlight and Eric Lippert's solutions:

    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));
        }
    }
Dave
  • 4,375
  • 3
  • 24
  • 30
3

A really other option is to have a proper OO design.

e.g. ask the MyNode to return all flatten.

Like this:

class MyNode
{
    public MyNode Parent;
    public IEnumerable<MyNode> Elements;
    int group = 1;

    public IEnumerable<MyNode> GetAllNodes()
    {
        if (Elements == null)
        {
            return Enumerable.Empty<MyNode>(); 
        }

        return Elements.SelectMany(e => e.GetAllNodes());
    }
}

Now you could ask the top level MyNode to get all the nodes.

var flatten = topNode.GetAllNodes();

If you can't edit the class, then this isn't an option. But otherwise, I think this is could be preferred of a separate (recursive) LINQ method.

This is using LINQ, So I think this answer is applicable here ;)

Julian
  • 33,915
  • 22
  • 119
  • 174
1

Combining Dave's and Ivan Stoev's answer in case you need the level of nesting and the list flattened "in order" and not reversed like in the answer given by 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();
            }
        }
    }
Corcus
  • 1,070
  • 7
  • 25
1

Here some ready to use implementation using Queue and returning the Flatten tree me first and then my children.

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);
            }
        }

    }
0
void Main()
{
    var allNodes = GetTreeNodes().Flatten(x => x.Elements);

    allNodes.Dump();
}

public static class ExtensionMethods
{
    public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> childrenSelector = null)
    {
        if (source == null)
        {
            return new List<T>();
        }

        var list = source;

        if (childrenSelector != null)
        {
            foreach (var item in source)
            {
                list = list.Concat(childrenSelector(item).Flatten(childrenSelector));
            }
        }

        return list;
    }
}

IEnumerable<MyNode> GetTreeNodes() {
    return new[] { 
        new MyNode { Elements = new[] { new MyNode() }},
        new MyNode { Elements = new[] { new MyNode(), new MyNode(), new MyNode() }}
    };
}

class MyNode
{
    public MyNode Parent;
    public IEnumerable<MyNode> Elements;
    int group = 1;
}
Tom Brothers
  • 5,929
  • 1
  • 20
  • 17
  • 1
    using a foreach in your extension means it is no longer 'delayed execution' (unless of course you use yield return). – Tri Q Tran Mar 11 '13 at 23:29
0

Building on Konamiman's answer, and the comment that the ordering is unexpected, here's a version with an explicit sort param:

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);
    }
}

And a sample usage:

var flattened = doc.TraverseAndFlatten(x => x.DependentDocuments, y => y.Document.DocDated).ToList();
rothschild86
  • 1,433
  • 16
  • 22
0

Below is Ivan Stoev's code with the additonal feature of telling the index of every object in the path. E.g. search for "Item_120":

Item_0--Item_00
        Item_01

Item_1--Item_10
        Item_11
        Item_12--Item_120

would return the item and an int array [1,2,0]. Obviously, nesting level is also available, as length of the array.

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();
    }
}
lisz
  • 435
  • 5
  • 9
  • Hi, @lisz, where do you paste this code ? I get errors such as "The modifier 'public' is not valid for this item", "The modifier 'static' is not valid for this item" – Kynao Sep 16 '18 at 07:49
0

Every once in awhile I try to scratch at this problem and devise my own solution that supports arbitrarily deep structures (no recursion), performs breadth first traversal, and doesn't abuse too many LINQ queries or preemptively execute recursion on the children. After digging around in the .NET source and trying many solutions, I've finally come up with this solution. It ended up being very close to Ian Stoev's answer (whose answer I only saw just now), however mine doesn't utilize infinite loops or have unusual code flow.

public static IEnumerable<T> Traverse<T>(
    this IEnumerable<T> source,
    Func<T, IEnumerable<T>> fnRecurse)
{
    if (source != null)
    {
        Stack<IEnumerator<T>> enumerators = new Stack<IEnumerator<T>>();
        try
        {
            enumerators.Push(source.GetEnumerator());
            while (enumerators.Count > 0)
            {
                var top = enumerators.Peek();
                while (top.MoveNext())
                {
                    yield return top.Current;

                    var children = fnRecurse(top.Current);
                    if (children != null)
                    {
                        top = children.GetEnumerator();
                        enumerators.Push(top);
                    }
                }

                enumerators.Pop().Dispose();
            }
        }
        finally
        {
            while (enumerators.Count > 0)
                enumerators.Pop().Dispose();
        }
    }
}

A working example can be found here.

Chris
  • 325
  • 1
  • 3
  • 11
0

Based on previous answer Pre-order flatten

        public static IEnumerable<T> Flatten<T>(
           this IEnumerable<T> e
        , Func<T, IEnumerable<T>> f
         ) => e.Concat(e.SelectMany(c => f(c).Flatten(f)));
tulipe
  • 676
  • 2
  • 8
  • 22