I have data that looks like the below:
a.b.c.d.e.f.g
b.c.d.e.f.g.h.x
c.d.e.q.s.n.m.y
a.b.c
I need to take this data and turn each and every level into a node in a treeview. So the tree looks something like:
a
b
c
d
e
...
b
c
d
....
if for example at the same leve there is another a, elements under this should be added as nodes to that branch. I have thought of the following:
Parse each line that is qualified by the dot character for each element and create an ordered list.
For each item in the list add it as a node in the current location.
Before adding check to make sure another item at the same level does not exist with the same name.
Add the next element until all items in the list are done, next elements being child to the first added item of the list.
I hope I was clear and let me know if it needs further clarification.
You can change the Node class to have the checks, if you want a list of children nodes, etc, add that as a HashSet, so you can easily make the check for uniqueness. Add a method in the Node class to do the AddChild and do the check on the HashSet.
public class Main
{
public Main()
{
string treeStr = "";
string[] strArr = { "a.b.c.d.e.f.g", "b.c.d.e.f.g.h.x" };
List<Node> nodes = new List<Node>();
Node currentNode;
foreach (var str in strArr)
{
string[] split = str.Split('.');
currentNode = null;
for (int i = 0; i < split.Length; i++)
{
var newNode = new Node { Value = str };
if (currentNode != null)
{
currentNode.Child = newNode;
}
else
{
nodes.Add(newNode);
}
currentNode = newNode;
}
}
}
}
public class Node
{
public string Value { get; set; }
public Node Child { get; set; }
}
I'm assuming the existence of the methods CreateRootNode and AddChildNode.
void ParseToTreeview(IEnumerable<string> data) {
foreach (var line in data) {
var names = line.Split('.');
for (var i = 0; i < names.Length; i++) {
TreeNode node = null;
if (i == 0)
node = CreateRootNode(name:names[i]);
else
node = AddChildNode(name:names[i], parentNode:node);
}
}
}
A recursive method to add all of these is what you need. Here's a sample:
Use:
string[] yourListOfData = { "a.b.c.d.e.f.g", "b.c.d.e.f.g.h.x", "c.d.e.q.s.n.m.y", "a.b.c" };
foreach(string x in yourListOfData)
PopulateTreeView(x, myTreeView.Nodes[0]);
Sample Method:
public void PopulateTreeView(string values, TreeNode parentNode )
{
string nodeValue = values;
string additionalData = values.Substring(value.Length - (value.Length - 2));
try
{
if (!string.IsNullOrEmpty(nodeValue))
{
TreeNode myNode = new TreeNode(nodeValue);
parentNode.Nodes.Add(myNode);
PopulateTreeView(additionalData, myNode);
}
} catch ( UnauthorizedAccessException ) {
parentNode.Nodes.Add( "Access denied" );
} // end catch
}
NOTE: code above is not tested, might need tweaking
Related
I'm taking lines (I:3, I:6, D:5, etc) from a text file and splitting them on the colon. Then, taking the number after the colon and pushing it to a node in a linked list. Depending on the line, it will insert(I) or delete(D) the node. However, I'm having trouble deleting a node. I created a method called deleteNode()
I reference this method in my if statement to check whether or not the command in the file starts with I or D to be told whether it gets inserted or deleted or not. I'm having trouble on how to reference the node to be deleted in llist.deletenode();
public class LinkedList
{
Node head; // the head of list
public class Node
{
public int data;
public Node next;
// constructor
public Node(int d)
{
data = d;
next = null;
} // end of constructor
}
public void printList()
{
// traversing list and printing the contents starting from head(1)
Node n = head;
int count = 0;
while (n != null)
{
count++;
Console.Write("Node" + count + ":" + " " + n.data + " ");
n = n.next;
}
}
public void push(int new_data)
{
// ads node to list
Node new_node = new Node(new_data); //allocate new node, put in data
new_node.next = head; //make next of new node as head
head = new_node; //moving head to point to the new node
}
public static void deleteNode(Node node, Node n)
{
// deletes node from list
// find the previous node
Node prev = node;
while (prev.next != null && prev.next != n)
{
prev = prev.next;
}
// Check if node really exists in Linked List
if (prev.next == null)
{
Console.WriteLine("Given node is not" +
"present in Linked List");
return;
}
// Remove node from Linked List
prev.next = prev.next.next;
// Free memory
GC.Collect();
return;
}
}
// main method to create a linked list with 3 nodes
public static void Main(String[] args)
{
// starting with an empty list
LinkedList llist = new LinkedList();
string[] lines = File.ReadAllLines(#"C:\\Users\project\text.txt");
foreach (string line in lines)
{
// splitting the lines on the colon
string[] bits = line.Split(':');
// taking the bit after the colon and parsing
// into an integer - the i is already parsed
int x = int.Parse(bits[1]); //the value after colon
if (bits[0] == "i")
{
llist.push(x);
}
else if (bits[0] == "d")
{
deleteNode(llist, existingNode); //error here
}
}
llist.printList();
}
I think the logic should probably match that of the push method (not sure why push isn't called Add or Insert instead, though...), in that it should look for the first node whose data matches the data we want to delete.
If we start at the head, we first want to determine if the head should be deleted. If so, then we reset the head to head.next and we're done!
Otherwise, we examine head.Next to see if it's the one to delete. If it is, then we set head.Next equal to head.Next.Next, effectively removing head.Next from our list (no node is pointing to it anymore).
If head.Next is not the node to delete, then we move to the next node and continue the process.
Here's an example:
public void DeleteNode(int nodeData)
{
// First check if the head is the node to delete
if (head != null && head.data == nodeData)
{
// If it is, set the head to the next node (effectively removing it from the list)
head = head.next;
return;
}
// Start at the head node
var current = head;
while (current != null)
{
// Get the next node
var next = current.next;
// See if the next node is the one to delte
if (next != null && next.data == nodeData)
{
// It is, so set the current node's Next pointer to the *next* node's
// Next pointer (effectively removing the Next node from the list)
current.next = next.next;
return;
}
// Update our current node to the next one and keep looking
current = next;
}
}
There are a lot of issues with your code ranging from aesthetic (name casing), to syntax errors, to design errors (static functions instead of methods).
I am offering some cleanup and an example below. The result is
Print List:
Node1: 0 Node2: 1 Node3: 2 Node4: 3 Node5: 4 Node6: 5
Deleting 4
Print List:
Node1: 0 Node2: 1 Node3: 2 Node4: 3 Node5: 5
and the sample code. The main addition is a separate function that finds the previous node FindPrevious() and use the existing Head of the list in DeleteNode().
Also changed fields into properties and PascalCasing as recommended per C# design rules.
namespace ConsoleApp1
{
public class Node
{
public int Data { get; set; }
public Node Next { get; set; }
// constructor
public Node(int data)
: this(null, data)
{ }
// always add a full constructor
public Node(Node next, int data)
{
this.Data = data;
this.Next = next;
}
}
public class LinkedList
{
public Node Head { get; set; }
public string PrintList()
{
// traversing list and printing the contents starting from head(1)
Node n = Head;
int count = 0;
StringBuilder sb = new StringBuilder();
while (n != null)
{
count++;
sb.Append("Node" + count + ":" + " " + n.Data + " ");
n = n.Next;
}
return sb.ToString();
}
// adds node to list
public void Push(int data)
{
//allocate new node, put in data
//and make next of new node as head
//moving head to point to the new node
Head = new Node(Head, data);
}
public Node FindPrevious(Node node)
{
Node n = Head;
while (n!=null)
{
if (n.Next == node)
{
return n;
}
n = n.Next;
}
return null;
}
public void DeleteNode(Node node)
{
if (node==null)
{
return;
}
Node prev = FindPrevious(node);
if (prev!=null)
{
// skip over node
prev.Next = node.Next;
}
}
}
static class Program
{
static void Main(string[] args)
{
LinkedList llist = new LinkedList();
llist.Push(5);
llist.Push(4);
llist.Push(3);
llist.Push(2);
llist.Push(1);
llist.Push(0);
Console.WriteLine($"Print List:");
Console.WriteLine(llist.PrintList());
Console.WriteLine();
var existingNode = llist.Head.Next.Next.Next.Next;
Console.WriteLine($"Deleting {existingNode.Data}");
llist.DeleteNode(existingNode);
Console.WriteLine($"Print List:");
Console.WriteLine(llist.PrintList());
}
}
}
DeleteNode method takes in two arguments. You are using deleteNode as an extension method but it's a simple method with two inputs.
deleteNode(llist, nodeYouWantToDelete);
Also, your loop that checks for D isn't right
if (bits[0] == "i") { //loop that checks if command starts with i (for inserting)
llist.push(x);
} else if(bits[0] == "d") { // CHECK bits[0] not [1]
// Get the node that matches value
// check here if the node already exists then delete
deleteNode(llist, existingNode);
}
update
public void push(int new_data) { //ads node to list
Node new_node = new Node(new_data); //allocate new node, put in data
new_node.next = null; //next should always be null.
// start from head and find the node whose next = null, point that to new_node
}
public void deleteNode(Node nodeToDelete) { //deletes node from list
// start from head and keep going next until node.next = nodeToDelete
// set node.next = node.next.next;
}
Working Solution for you
public class LinkedList
{
Node head;
Node last;
public class Node
{
public int data;
public Node next;
public Node(int d)
{
data = d;
next = null;
} // end of constructor
}
public void print()
{
// traversing list and printing the contents starting from head(1)
Node n = head;
int count = 0;
while (n != null)
{
Console.Write("Node" + count++ + ":" + " " + n.data + " ");
n = n.next;
}
}
public void push(int new_data)
{
Node thisNode = new Node(new_data);
if (head == null)
{
head = thisNode;
last = head;
return;
}
last.next = thisNode;
last = last.next;
}
public void delete(Node node)
{
if (head.data == node.data)
head = head.next;
else
{
Node iterate = head;
bool deleted = false;
while (iterate.next != null)
{
if (iterate.next.data == node.data && !deleted)
{
iterate.next = iterate.next.next;
deleted = true;
continue;
}
iterate.next = iterate.next.next;
}
last = iterate;
if (!deleted)
{
Console.WriteLine("Given node is not " +
"present in Linked List");
}
// Free memory
GC.Collect();
return;
}
}
}
//and use it in the main like,
public static void Main(string[] args)
{
LinkedList llist = new LinkedList();
string[] lines = new[] { "i:1", "i:3", "d:3", "i:2", "i:1", "d:3" };
foreach(string line in lines)
{
string[] split = line.Split(':');
if (split[0] == "i") // INSERTS At the end of the list.
llist.push(int.Parse(split[1]));
else if (split[0] == "d") // Deletes the "FIRST" occurence of said number
llist.delete(new LinkedList.Node(int.Parse(split[1])));
}
Console.Read();
}
public void Convert(Node)
{
StartGrouping();
DoSomething();
int childCount = node.GetChildCount();
for (int i = 0; i < childCount; i++)
{
Convert(node.GetChild(i));
}
if(last_node)
{
EndGrouping();
}
}
What is the correct way to check when we reach the last node (as in the picture).
If you want to find the last node that has no children: i.e. find the last node of the current level and if it has children go deeper otherwise return it, then you can do it easily:
private Node FindLast(Node[] nodes)
{
var node = nodes.GetLast();
if (node.HasChildren())
{
return FindLast(node.GetChildren());
}
return node;
}
EDIT:
Right thanks for helping earlier, I have been using and the step into and step over and it looks to be working but the nodes are not being deleted and I'm not sure why.
I actually use 5 arguments for the BST but just using the one for testing purposes. It compares and finds if it has any children no problem. Just wont set it to null.
only testing nodes with 0 or 1 children.
main
Tree aTree = new Tree();
aTree.InsertNode("a");
aTree.InsertNode("s");
aTree.InsertNode("3");
aTree.InsertNode("1");
aTree.InsertNode("p");
aTree.PreorderTraversal();
aTree.RemoveNode("p");
aTree.RemoveNode("3");
aTree.PreorderTraversal();
Console.ReadKey();
My Delete Methods are:
Tree Node
public void Remove(TreeNode root, TreeNode Delete) {
if (Data == null) {
}
if (Delete.Data.CompareTo(root.Data) < 0) {
root.nodeLeft.Remove(root.nodeLeft, Delete);
}
if (Delete.Data.CompareTo(root.Data) > 0) {
root.nodeRight.Remove(root.nodeRight, Delete);
}
if (Delete.Data == root.Data) {
//No child nodes
if (root.nodeLeft == null && root.nodeRight == null) {
root = null;
}
else if (root.nodeLeft == null)
{
TreeNode temp = root;
root = root.nodeRight;
root.nodeRight = null;
temp = null;
}
//No right child
else if (root.nodeRight == null)
{
TreeNode temp = root;
root = root.nodeLeft;
root.nodeLeft = null;
temp = null;
}
//Has both child nodes
else
{
TreeNode min = minvalue(root.nodeRight);
root.Data = min.Data;
root.nodeRight.Remove(root.nodeRight, min);
}
}
}
Find Min
public TreeNode minvalue(TreeNode node)
{
TreeNode current = node;
/* loop down to find the leftmost leaf */
while (current.nodeLeft != null)
{
current = current.nodeLeft;
}
return current;
}
Tree
public void RemoveNode(string Nation)
{
TreeNode Delete = new TreeNode(Nation);
root.Remove(root, Delete);
}
Remove is of return type void, but you're trying to assign it to root.nodeLeft and root.nodeRight, causing your type conversion error.
In general your Remove function needs to return the root of the sub-tree as the result, as in
public void Remove(TreeNode root, TreeNode Delete) {
if (Data == null) {
return null;
}
if (Delete.Data.CompareTo(root.Data) < 0) {
root.nodeLeft = (root.nodeLeft.Remove(root.nodeLeft, Delete));
return root;
}
... and so on.
Otherwise, since your nodes don't refer to their parents, there would be no way for the parent to know that the child node is gone, or that a new node is now at the root of the sub-tree.
I am having some trouble setting child nodes using C#. I am trying to build a tree of nodes where each node holds an int value and can have up to a number of children equal to it's value.
My issue appears when I iterate in a node looking for empty(null) children so that I may add a new node into that spot. I can find and return the null node, but when I set the new node to it, it loses connection to the parent node.
So if I add 1 node, then it is linked to my head node, but if I try to add a second it does not become a child of the head node. I am trying to build this with unit tests so here is the test code showing that indeed the head does not show the new node as it's child (also confirmed with visual studios debugger):
[TestMethod]
public void addSecondNodeAsFirstChildToHead()
{
//arange
Problem3 p3 = new Problem3();
p3.addNode(2, p3._head);
Node expected = null;
Node expected2 = p3._head.children[0];
int count = 2;
//act
Node actual = p3.addNode(1, p3._head);
Node expected3 = p3._head.children[0];
//assert
Assert.AreNotEqual(expected, actual, "Node not added"); //pass
Assert.AreNotEqual(expected2, actual, "Node not added as first child"); //pass
Assert.AreEqual(expected3, actual, "Node not added as first child"); //FAILS HERE
Assert.AreEqual(count, p3.nodeCount, "Not added"); //pass
}
Here is my code.
public class Node
{
public Node[] children;
public int data;
public Node(int value)
{
data = value;
children = new Node[value];
for(int i = 0; i < value; i++)
{
children[i] = null;
}
}
}
public class Problem3
{
public Node _head;
public int nodeCount;
public Problem3()
{
_head = null;
nodeCount = 0;
}
public Node addNode(int value, Node currentNode)
{
if(value < 1)
{
return null;
}
Node temp = new Node(value);
//check head
if (_head == null)
{
_head = temp;
nodeCount++;
return _head;
}
//start at Current Node
if (currentNode == null)
{
currentNode = temp;
nodeCount++;
return currentNode;
}
//find first empty child
Node emptyChild = findEmptyChild(currentNode);
emptyChild = temp;
nodeCount++;
return emptyChild;
}
public Node findEmptyChild(Node currentNode)
{
Node emptyChild = null;
//find first empty child of current node
for (int i = 0; i < currentNode.children.Length; i++)
{
if (currentNode.children[i] == null)
{
return currentNode.children[i];
}
}
//move to first child and check it's children for an empty
//**this causes values to always accumulate on left side of the tree
emptyChild = findEmptyChild(currentNode.children[0]);
return emptyChild;
}
I feel the problem is I am trying to treat the nodes as pointers like I would in C++ but that it is not working as I expect.
It is impossible for a function to return a handle (or a pointer) to something that does not yet exist. Either you initialize non existent value inside the function, or you provide enough variables for it to be initialized outside of the function.
One solution would be to rename the function findEmptyChild to something like initializeEmptyChild(Node currentNode, Node newNode), adding one more Node parameter to it (when calling it that would be temp value), and in the loop before return you initialize the previously empty Node, currentNode.children[i] = newNode.
Another solution would be not to return just one Node but two values, a parent node and an index where empty child is found, Tuple<Node, int> findEmptyChild(Node currentNode), and in the loop instead of return currentNode.children[i] you do return new Tuple<Node, int>(currentNode, i). When calling the function you would change the code to
var parentAndIndex = findEmptyChild(currentNode);
parentAndIndex.Item1.children[parentAndIndex.Item2] = temp;
Look at this part of your code:
Node temp = new Node(value);
//...
Node emptyChild = findEmptyChild(currentNode);
emptyChild = temp;
You are assigning the emptyChild to a new node, doing so you will "loose" the connection with any parent node. You should write something like this:
emptyChild.data = temp.data;
emptyChild.children = temp.children;
As others said, your approach using null checking could be improved. You mentioned that Node.data holds the numbers of children of a given node, so you could simply say that when you have Node.data == 0, that node should be treated as being null, or empty. For example, instead of having:
rootNode.children[0] = null; // rootNode can have a lot of children
rootNode.children[1] = null;
//...
you would have:
rootNode.children[0] = new Node(0);
rootNode.children[1] = new Node(0);
//...
At this point your code will look similar to this:
public class Node
{
public Node[] children;
public int data;
public Node(int value)
{
data = value;
children = new Node[value];
// Instead of "pointing" to null,
// create a new empty node for each child.
for (int i = 0; i < value; i++)
{
children[i] = new Node(0);
}
}
}
public class Problem3
{
public Node _head;
public int nodeCount;
public Problem3()
{
_head = null;
nodeCount = 0;
}
public Node addNode(int value, Node currentNode)
{
if (value < 1)
{
return null;
}
Node temp = new Node(value);
//check head
if (_head == null)
{
_head = temp;
nodeCount++;
return _head;
}
//start at Current Node
if (currentNode == null)
{
currentNode = temp;
nodeCount++;
return currentNode;
}
//find first empty child
Node emptyChild = findEmptyChild(currentNode);
if (emptyChild != null)
{
emptyChild.data = temp.data;
emptyChild.children = temp.children;
nodeCount++;
}
return emptyChild;
}
public Node findEmptyChild(Node currentNode)
{
// Null checking.
if (currentNode == null)
return null;
// If current node is empty, return it.
if (currentNode.data == 0)
return currentNode;
// If current node is non-empty, check its children.
// If no child is empty, null will be returned.
// You could change this method to check even the
// children of the children and so on...
return currentNode.children.FirstOrDefault(node => node.data == 0);
}
}
Let's look now at the testing part (please see the comments for clarification):
[TestMethod]
public void addSecondNodeAsFirstChildToHead()
{
//arange
Problem3 p3 = new Problem3();
p3.addNode(2, p3._head); // Adding two empty nodes to _head, this means that now _head can
// contain two nodes, but for now they are empty (think of them as
// being "null", even if it's not true)
Node expected = null;
Node expected2 = p3._head.children[0]; // Should be the first of the empty nodes added before.
// Be careful: if you later change p3._head.children[0]
// values, expected2 will change too, because they are
// now pointing to the same object in memory
int count = 2;
//act
Node actual = p3.addNode(1, p3._head); // Now we add a non-empty node to _head, this means
// that we will have a total of two non-empty nodes:
// this one fresly added and _head (added before)
Node expected3 = p3._head.children[0]; // This was an empty node, but now should be non-empty
// because of the statement above. Now expected2 should
// be non-empty too.
//assert
Assert.AreNotEqual(expected, actual, "Node not added"); //pass
// This assert won't work anymore, because expected2, expected 3 and actual
// are now pointing at the same object in memory: p3._head.children[0].
// In your code, this assert was working because
// In order to make it work, you should replace this statement:
// Node expected2 = p3._head.children[0];
// with this one:
// Node expected2 = new Node(0); // Create an empty node.
// expected2.data = p3._head.children[0].data; // Copy data
// expected2.children = p3._head.children[0].children;
// This will make a copy of the node instead of changing its reference.
Assert.AreNotEqual(expected2, actual, "Node not added as first child");
// Now this will work.
Assert.AreEqual(expected3, actual, "Node not added as first child");
Assert.AreEqual(count, p3.nodeCount, "Not added"); //pass
}
I would like to create a method which collect custom childnode values from an xml file and rewrite whit datas from a form. I had an idea thet I collect the datas in an ArrayList and give it to the method. But I cant change it in a foreach, because it throws ArgumentOutOfRangeException( although the ArraList contains 8 elements and the incremental variable's value also 8). So I would ask for help.
Here is the Code:
public static void Search(ArrayList nodeIds, ArrayList values)
{
XDocument doc = XDocument.Load("Options.xml");
int i = 0;
foreach (XElement option in doc.Descendants("BasicOptions"))
{
foreach(string nodeId in nodeIds)
{
if (option.Attribute("id").Value == nodeId)
{
foreach (XElement prop in option.Nodes())
{
prop.Value = values[i].ToString();
i++;
}
}
}
}
doc.Save("Options.xml");
}
It seems to me that i will go out of range without question because it is declared externally to 3 foreach statements and used within the center foreach. You should rethink your approach.
I suggest, but without knowing your incoming values or why your calling this, to redclare your internal foreach as a for statement like the following:
public static void Search(ArrayList nodeIds, ArrayList values)
{
XDocument doc = XDocument.Load("Options.xml");
foreach (XElement option in doc.Descendants("BasicOptions"))
{
foreach (string nodeId in nodeIds)
{
if (option.Attribute("id").Value == nodeId)
{
var nodes = option.Nodes().ToList();
for (int i = 0; i < nodes.Count && i < values.Count; i++)
{
XElement node = (XElement)nodes[i];
node.Value = values[i].ToString();
}
}
}
}
doc.Save("Options.xml");
}