How to traverse a multi-hierarchy array in C# - c#

Say rootNode is a multi-hierarchy data structure.
rootNode.Add(node1);
rootNode.Add(node2);
node1.Add(node3);
node1.Add(node4);
node3.Add(node5);
If use foreach to traverse rootNode will only get node1, node2. How do I traverse all nodes in rootNode?
foreach(var node in rootNode){...}

You can traverse the tree using recursion.
VisitNode(Node n){
foreach(var cn in n.Children){
VisitNode(cn);
}
//Do what you want to do with your node here
Console.Writeline(n.Value);
}
Here is an example of breadth first traversal.

Make a recursive call:
TraverseNodes(parentNode)
{
for each (Node node in parentNode)
{
if (node.Nodes.Count>0)
TraverseNodes(node);
}
}

You can setup a simple recursive function
//Pseudo-code
public void traverse(Node n)
{
if(n hasChildren)
{
foreach(Node child in n.children)
{
traverse(child);
}
}
}

The easiest way would be recursion. What is recursion? See this answer for an example.
public void TraverseNodes(Node parentNode)
{
//iterate through child nodes
foreach(var node in parentNode)
{
//action
//iterate though child's child nodes.
TraverseNodes(node);
}
}
Basically you are performing the same operation on all the child items by calling the same method (TraverseNodes) on all of the parent items (starting from the first parent).

If your structure isn't too deep then you can safely use the recursive method given in the other answers.
If, however, your structure is potentially very deep then using recursion runs the risk of blowing the call stack and causing a StackOverflowException.
Here's an example of a non-recursive way of traversing your structure:
var stack = new Stack<TNode>();
stack.Push(rootNode);
while (stack.Count > 0)
{
var node = stack.Pop();
// do whatever you need to do with each node here
foreach (var childNode in node)
{
stack.Push(childNode);
}
}

Even if the (working) recursive methods have been posted yet, I'd like to contribute two additional methods.
The first one "pushes" each node in the tree into an Action<Node> that can "consume" it.
public void TraverseWithAction(Action<Node> nodeAction) {
nodeAction(this);
foreach(Node n in this.children) {
n.TraverseWithAction(nodeAction);
}
}
Usage example:
rootNode.TraverseWithAction(n => buffer.Append(n.ToString()));
The second one provides an IEnumerable<Node> over the root node and all its child nodes, recursively. (And, yes, there are only two loops but they can handle trees deeper than two.)
public IEnumerable<Node> TraverseAsEnumerable() {
yield return this;
foreach(Node n in this.children) {
foreach (Node n2 in n.TraverseAsEnumerable()) {
yield return n2;
}
}
}
Usage example:
foreach (Node n in rootNode.TraverseAsEnumerable()) {
// do something with n
}
Both methods use recursion so they might fail on very deep structures.

Related

Writing a tree structure to disk using C# and recursion

I'm fairly new to C# and I'm attempting to take the tree structure I've created from a list, and write that to the file system. (I've used this answer).
That is, I want to create the directories and subdirectories in the proper depth. However, I'm getting stuck on depth and cannot think of how to loop through each iteration of the depth, then back out to start writing again, without just writing all the 0-depth directories, then all the 1-depth directories, etc., in the same location.
I'm thinking there's another recursive routine/method/function, but I just can't wrap my head around it. I'd like to get to the next level (ha) in my understanding of programming.
static void Test(IEnumerable<TreeItem<category>> categories, int deep = 0)
{
foreach (var c in categories)
{
for (int i = 0; i < deep; ++i) {
System.IO.Directory.CreateDirectory(c.Item.Name);
}
Test(c.Children, deep + 1);
}
}
Because Directory.CreateDirectory creates all the parent directories for you, assuming you have the full path stored for each node, you only need to pass leaf nodes (nodes with no children) to it.
You can write a recursive search to retrieve leaf nodes, then pass that list to CreateDirectory.
static IEnumerable<TreeItem<category>> GetLeafs(IEnumerable<TreeItem<category>> tree)
{
foreach (var item in tree)
{
if (item.Children.Any())
{
// this is not a leaf, so find the leaves in its descendants
foreach (var leaf in GetLeafs(item.Children))
yield return leaf;
}
else
{
// no children, so this is a leaf
yield item;
}
}
}
static void CreateDirectories(IEnumerable<TreeItem<category>> categories)
{
foreach (var leaf in GetLeafs(categories))
{
System.IO.Directory.CreateDirectory(leaf.Item.Name);
}
}
If you don't have the full path against each node, then you can use the same structure as GetLeafs (although it turns out much simpler, because you don't need to return anything back up the call-chain) to recursively walk the tree, creating directories as you go deeper:
static void CreateDirectories(IEnumerable<TreeItem<category>> tree)
{
foreach (var item in tree)
{
Directory.Create(c.Item.Name);
CreateDirectories(item.Children);
}
}
This method is simpler, but will result in more calls to Directory.Create, which could conceivably have a performance impact.

Get list of all checked nodes and its subnodes in treeview

I have a treeview list check boxes and the list contains nodes, subnodes and in some cases subnode of subnode. When user check some items i want to get list of selected items.
On this why I get only selcted items of main node:
foreach (System.Windows.Forms.TreeNode aNode in tvSastavnica.Nodes)
{
if (aNode.Checked == true)
{
Console.WriteLine(aNode.Text);
}
}
How to travers through whole treeview and get checked items in subnodes?
If you like LINQ, you can create an extension method that traverses the whole treeview:
internal static IEnumerable<TreeNode> Descendants(this TreeNodeCollection c)
{
foreach (var node in c.OfType<TreeNode>())
{
yield return node;
foreach (var child in node.Nodes.Descendants())
{
yield return child;
}
}
}
Then you can perform every operations you want using LINQ. In your case, getting a list of selected nodes is easy:
var selectedNodes = myTreeView.Nodes.Descendants()
.Where(n => n.Checked)
.Select(n => n.Text)
.ToList();
An advantage of this approach is it is generic.
However, because the Descendant() method traverses the whole tree, it might be a bit less efficient than the answer given by #mybirthname because it only cares about nodes that are checked with their parents. I dont known if your use case includes this constraint.
EDIT: Now #mybirthname answer has been edited, it is doing the same. Now you have the loop and the LINQ solution, both are recursive.
public void GetCheckedNodes(TreeNodeCollection nodes)
{
foreach(System.Windows.Forms.TreeNode aNode in nodes)
{
//edit
if(!aNode.Checked)
continue;
Console.WriteLine(aNode.Text);
if(aNode.Nodes.Count != 0)
GetCheckedNodes(aNode.Nodes);
}
}
You don't make look back into the child notes, using recursion you can do it.
You need method like this ! In your code just call once GetCheckedNodes(tvSastavnica.Nodes) and all checked nodes should be displayed !
my way:
void LookupChecks(TreeNodeCollection nodes, List<TreeNode> list)
{
foreach (TreeNode node in nodes)
{
if (node.Checked)
list.Add(node);
LookupChecks(node.Nodes, list);
}
}
useage:
var list = new List<TreeNode>();
LookupChecks(TreeView.Nodes, list);

C# Adding Visible Nodes to Node Array

I'm trying to create a function that will add each visible node in a tree to a node array and then return it.
This is the code I have so far, but struggling to figure out how to add them.
Note: The tree has a maximum of 8 nodes.
private Node[] activeClients(AdvTree tree)
{
Node[] activeClients = new Node[8];
foreach (Node client in tree.Nodes)
{
if (client.IsVisible)
{
//Add Visible Node to activeClients Node Array
}
}
return activeClients;
}
May be something like:
var visibleNodes = tree.Nodes.Where(client=>client.IsVisible)
especially if you are talking about small numbers (8 elements) and not compute intensive function, dynamic array (or vector) like List<T>, IEnumerable<T> is a right choice.
And in this way, your code also scales better in the future.
I actually figured out I didn't need a Node Array, but thanks for the help guys.
I used NodeCollection instead and it worked perfect for my needs.
private NodeCollection activeClients(AdvTree tree)
{
NodeCollection activeClients = new NodeCollection();
foreach (Node client in tree.Nodes)
{
if (client.IsVisible)
{
//Add Visible Node to activeClients Node Array
activeClients.Add(client, eTreeAction.Code);
}
}
return activeClients;
}

TreeNode Remove() not working

Long story short:
I traverse a generic tree and collect some nodes to a list according to a filter
after a complete traversal I start removing each node one by one
remove collected nodes
foreach (TreeData nd in nodeBucket.Reverse<TreeData>())
{
if (nd.node.Parent != null)
{
nd.node.Remove();
}
}
The tree has about 2000 nodes. Somehow when all nodes should be deleted there are a couple of nodes that don't get removed, Remove() is called upon them and they have a null parent afterwords. The nodes that don't get deleted are always the same.
Also tried node.Parent.Nodes.Remove(node), no luck.
tree.BeginUpdate();
tree.EndUpdate();
tree.Invalidate();
tree.Refresh();
tree.Update();
not working.
TreeData struct looks like this:
struct TreeData
{
public TreeData( TreeNode node)
{
this.node = node;
this.parent = node.Parent;
this.level = node.Level;
}
public TreeNode node;
public TreeNode parent;
public int level;
}
Issue fixed: the problem was related to duplicate nodes (the filtering was checking node names).
If you want to keep the current node but only need to remove the children
While(node.Nodes.Count>0)
node.Nodes[0].Remove();

Why isn't this recursion working in C#?

public static bool AllNodesChecked(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
if (!node.Checked)
{
return false;
}
AllNodesChecked(node.Nodes);
}
return true;
}
Test tree is
A1(checked) -> B1(unchecked)
A2(checked)
A3(checked)
but it isn't returning when it hits node B1.
EDIT: Thank you all for helping my tired brain. Recursion should only be attempted early in the day after a cold shower.
You are ignoring the return value of AllNodesChecked in the recursive call:
public static bool AllNodesChecked(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
if (!node.Checked || !AllNodesChecked(node.Nodes))
return false;
return true;
}
The return statement only returns from the current method in the call stack to the immediate caller. It doesn't suddenly return from all other calls above in the call stack.
Change:
AllNodesChecked(node.Nodes);
To:
if(!AllNodesChecked(node.Nodes))
return false;
I would take a slightly different approach here. What I'd do is I'd first write code that turns your tree (which I assume really is a tree, not an arbitrary graph) into a sequence of nodes. Something like:
static IEnumerable<Node> AllNodes(this Node node)
{
var stack = new Stack<Node>();
stack.Push(node);
while(stack.Count > 0)
{
var current = stack.Pop();
yield return current;
foreach(var child in current.Nodes)
stack.Push(child);
}
}
and now you can use sequence operators:
bool allChecked = root.AllNodes().All(x=>x.Checked);
No recursion, no problem.
You're not evaluating the result of the recursive call to check child nodes.
Try this:
public static bool AllNodesChecked(TreeNodeCollection nodes)
{
foreach (TreeNode node in nodes)
{
if (node.Checked == false || !AllNodesChecked(node.Nodes))
{
return false;
}
}
return true;
}
I have to add my two cents.... Learn to functional programming IMHO.
public static bool AllNodesChecked(TreeNodeCollection nodes)
{
return nodes.All(i => i.Checked && AllNodesChecked(i.Nodes));
}

Categories

Resources