0

I have this class:

public class Node
{
    public string Name {get; set;}
    public Node Left {get; set;}
    public Node Right{get; set;}
} 

Now I have tree that each node has a left and right node, and each of them has left and right nodes and so on.

I want to get Names of all node in the tree, I couldn't do it with SelectMany. I can do this in several ways like using a recursive function, but I'm really curious to know how is it done using Linq.

tree.SelectMany(x=> new List<Node> {x.Left, x.Right}); 

the above code just returns 2 Nodes (left and right nodes of the parent).

Ashkan Mobayen Khiabani
  • 33,575
  • 33
  • 102
  • 171
  • 3
    There's no builtin recursive LINQ method. So the best was if you just implement a recursive method yourself and call that. – Tim Schmelter Sep 28 '18 at 16:12
  • @TimSchmelter I actually did that and got what I wanted, but I was wondering if there is a way that I'm not aware of to do it with Linq and not callind another method. – Ashkan Mobayen Khiabani Sep 28 '18 at 16:14
  • You can find LINQ approaches on Stackoverflow. But they're pretty complex and often inefficient. Recursion and LINQ don't play well together, so my suggestion is to use your method. – Tim Schmelter Sep 28 '18 at 16:16
  • @TimSchmelter Thanks a lot for your comment. as I said a have already done that, I was thinking that there is a simple way that I'm missing and I just asked the question for learning purpose and not for the project. so far I have chosen a good approach then. – Ashkan Mobayen Khiabani Sep 28 '18 at 16:18

2 Answers2

1

Everything in LINQ is based on the interface IEnumerable. So for your LINQ to work, you must translate your root Node tree into something that is IEnumerable<Node>. We can do this with an extension method.

public static class NodeHelper
{
    public static IEnumerable<Node> ToEnumerable(this Node node)
    {
        var stack = new Stack<Node>();
        if (node != null) stack.Push(node);
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            yield return current;
            if (current.Left != null) stack.Push(current.Left);
            if (current.Right != null) stack.Push(current.Right);
        }
    }
}

Once you have an IEnumerable<Node>, you can just do a simple Select()

foreach (var name in tree.ToEnumerable().Select(node => node.Name))
{
    Console.WriteLine(name);
}

You do not need to use SelectMany since your Node does not have an IEnumerable<Node> of child nodes, just left and right nodes. We use SelectMany when we need to coalesce many sequences into a single sequence.

Hope this helps.

James P
  • 71
  • 4
0

Here's what I found:

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

    public IEnumerable<Node> GetSubTree()
    {
         return Children.SelectMany(c => c.GetSubTree()).Concat(new[] {this});
    }

    public List<Node> Children {get;set;}
}

class Tree
{
    public Tree() {Roots = new List<Node>();}

    public IEnumerable<Node> GetAllNodes()
    {
         return Roots.SelectMany(root => root.GetSubTree());
    }

    List<Node> Roots {get;set;}
}

If I'm right about it: When calling GetAllNodes() from Tree class, it will get all nodes from each roots in the tree. Then you can get the names with this if you have an attribute called Name obviously:

List<string> Names = YourList.Select(x => x.Name);

Hope it helps !