I want to implement a method that will enable me to find a node in a tree. The way I do it is recursively using global variables to know when to stop.
I have the class:
class Node    // represents a node in the tree
{ 
     // constructor
     public Node() {
          Children = new List<Node>();
     }
     public List<Node> Children; 
     public string Name;
     public string Content;            
}
And the method I have right now is:
    private bool IsNodeFound = false; // global variable that I use to decide when to stop
    // method to find a particular node in the tree
    private void Find(Node node, string stringToFind, Action<Node> foundNode)
    {
        if(IsNodeFound)
           return;
        if (node.Content.Contains(stringToFind)){
            foundNode(node); 
            IsNodeFound =true;               
        }
        foreach (var child in node.Children)
        {
            if (child.Content.Contains(stringToFind)){
                foundNode(node);
                IsNodeFound =true;               
            }
            Find(child, stringToFind, foundNode);
        }
    }
and the way I use the Find method is like:
   // root is a node that contain children and those children also contain children
   // root is the "root" of the tree
   IsNodeFound =false;
   Node nodeToFind = null;
   Find(root, "some string to look for", (x)=> nodeToFind=x);
So my question is how can I make this method more elegant. I will like the signature of the method to look like:
   public Node FindNode(Node rootNode);
I think it is to redundant what am I doing and there is probably a better way of creating that method. Or perhaps I could alter the Node class so that I can achieve the same thing with a linq query.
I would do it this way:
Write an instance method to generate a subtree of a node (you could make it an extension if you don't control the Node class):
public IEnumerable<Node> GetNodeAndDescendants() // Note that this method is lazy
{
     return new[] { this }
            .Concat(Children.SelectMany(child => child.GetNodeAndDescendants()));    
}
Then you can just find nodes with a little bit of LINQ:
var foundNode = rootNode.GetNodeAndDescendants()
                        .FirstOrDefault(node => node.Content.Contains(stringToFind));
if(foundNode != null)
{
    DoSomething(foundNode);
}
                        You can use one of the other answers that use Linq, or you can use a depth-first search mechanism using recursion:
public Node Find(string stringToFind)
{
    // find the string, starting with the current instance
    return Find(this, stringToFind);
}
// Search for a string in the specified node and all of its children
public Node Find(Node node, string stringToFind)
{
    if (node.Content.Contains(stringToFind))
        return node;
    foreach (var child in node.Children) 
    { 
        var result = Find(child, stringToFind);
        if (result != null)
            return result;
    }
    return null;
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With