C# How to find a Class' non created objects? - c#

I have this class
class Node
{
public bool is_end;
public int prefix_count;
public Node[] child = new Node[26];
}
My problem is, the class only creates Nodes if it is necessary, but later in the program, i need to check all the created Nodes, though since i don't know which ones are created, i can't use a cycle for it.
Is there a way for me to know which Nodes are created, is there a code to check if this Node exists or not.

Try using a List<Node> instead of a fixed length array. Then you can use methods such as
var nodeList = new List<Node>();
nodeList.Add(someRandomNode);
if (nodeList.Contains(someRandomNode))
{
// your logic
}
You may also iterate over your Node collection.
foreach(var node in nodeList) { }

In this case, your child array is pre-created, but each individual node is not allocated. You can check for null to see if a Node has been created:
bool ChildCreated(Node parent, int childIndex)
{
return parent.child[childIndex] != null;
}
I have to find out which child nodes are created, for example a cycle from 0 to 25, and it has to give a message like node[2] and node[11] are created, others aren't.
Using this method, you could easily do:
var created = Enumerable.Range(0, node.child.Length).Where(i => ChildCreated(node, i)).ToList();
if (!created.Any())
{
Console.WriteLine("No children created.");
}
else
{
Console.WriteLine("Children {0} created, all others aren't.", string.Join(", ", created));
}

All you need to do is to check if Node[] element is null:
int nodeIndex = 0;
if(child[nodeIndex] == null)
{
// node doesn't exist
}
else
{
// node exists
}

Related

Sort specific Node in TreeView?

I would like to know if there is any method to sort (A-Z) a specific node of a TreeView.
The node I want to order is the node "Node1 \ z"
To display it like this:
H
N
Y
Z
Thank you
Your question is how to apply an alpha sort (A-Z) to a specific single node in a TreeView.
There are many ways to do this and the comments mention some good ones. Here is a solution that efficiently locates the target node using its fully-qualified path in the tree hierarchy. If found, it applies an individualized sort method (specified at runtime) to its children. But first, it must ensure that the TreeView will permit such reordering by setting its Sorted property to false. An advantage of this approach is that it avoids making a call the Sort method for the entire TreeView because that would add an unnecessary layer of complexity.
Find the target node
Given a rule for the TreeView that the fully-qualified path to any node (for example, #"Node1\z") is unique, the target can be obtained very efficiently by making an extension for TreeView that returns the specified node (or null if not found).
public static TreeNode Find(this TreeView treeView, string path)
{
var parse = path.Split('\\');
var nodes = treeView.Nodes;
TreeNode node = null;
foreach (var text in parse)
{
node = nodes.Cast<TreeNode>().FirstOrDefault(node => node.Text == text);
if (node == null) break;
nodes = node.Nodes;
}
return node;
}
Sort children of target node
This extension for TreeNode sorts its children according to the comparer Func passed in as an argument.
public static void Sort(
this TreeNode node,
Func<TreeNode, TreeNode, int> sorter)
{
// Make sure the TreeView will allow reordering
if (node.TreeView != null)
{
node.TreeView.Sorted = false;
}
// Copy the nodes to a list
var list = node.Nodes.Cast<TreeNode>().ToList();
// Sort the list however the `Sorter` says to.
list.Sort((a, b) => sorter(a, b));
// Clear the 'old' order
node.Nodes.Clear();
// Install the 'new' order
foreach (var sorted in list)
{
node.Nodes.Add(sorted);
}
}
Complete method to sort (A-Z) a specific node of a TreeView.
So, to answer the question Is there any method to sort a specific node of a TreeView from A-Z let's say we manufacture such a method by making an extension for TreeView that does just that. For the path argument specify the entire hierarchal path like #"Node1" or #"Node1\z".
public static bool SortIfNodeFound(
this TreeView treeView,
string path,
Func<TreeNode, TreeNode, int> sorter)
{
var node = treeView.Find(path);
node?.Sort(sorter);
return node != null;
}
Once you call the TreeView.Sort method, you actually apply the sorted TREEVIEWSTATE through the setter of the hidden Sorted property. The control by that sorts all the nodes using the default sorter, the ascending alphabetical sort. Any attempts afterwards to sort the nodes in a different way fail. You'll note that nothing happens when you remove, sort, and reinsert child nodes of a specific node because once you insert them, the default sorter will interfere and revert your different sort. Again, all the mentioned is when you call the .Sort method before any other sorting routines.
To override this behavior, you need to provide a custom node sorter for the TreeView.TreeViewNodeSorter property. An example that allows you to sort the tree or children of node in ascending or descending orders.
public class TreeNodeComparer : IComparer
{
public TreeNodeComparer(SortOrder sortOrder = SortOrder.Ascending) : base()
{
SortOrder = sortOrder;
}
public int Compare(object x, object y)
{
var xn = x as TreeNode;
var yn = y as TreeNode;
switch (SortOrder)
{
case SortOrder.Descending:
return string.Compare(xn.Text, yn.Text) * -1;
case SortOrder.Ascending:
return string.Compare(xn.Text, yn.Text);
default:
return 1;
}
}
public SortOrder SortOrder { get; set; } = SortOrder.Ascending;
}
Note return 1; here in case SortOrder.None is necessary to not revert sorting the child nodes of a specific node.
A couple of extension methods for the TreeView and TreeNode types.
public static class TreeViewExtensions
{
public static void Sort(this TreeView self, SortOrder order = SortOrder.Ascending)
{
self.TreeViewNodeSorter = new TreeNodeComparer(order);
self.Sort();
}
public static void Sort(this TreeNode self, SortOrder order = SortOrder.Ascending)
{
List<TreeNode> tmp;
TreeView tv = self.TreeView;
if (order == SortOrder.Descending)
tmp = self.Nodes.Cast<TreeNode>().OrderByDescending(n => n.Text).ToList();
else
tmp = self.Nodes.Cast<TreeNode>().OrderBy(n => n.Text).ToList();
var sorter = tv.TreeViewNodeSorter as TreeNodeComparer ?? new TreeNodeComparer();
sorter.SortOrder = SortOrder.None;
tv.TreeViewNodeSorter = sorter;
tv.BeginUpdate();
self.Nodes.Clear();
self.Nodes.AddRange(tmp.ToArray());
tv.EndUpdate();
}
}
You can call them as follows:
// To sort the whole thing...
YourTreeView.Sort(SortOrder.Descending);
// Or the children of the selected node for example...
YourTreeView.SelectedNode.Sort(SortOrder.Ascending);

Counting Nested List Objects While Traversing

I am working on a project in C# where I have an object with properties, one of which is called Children which is the same as the parent object. For example:
public class ObjectInformation
{
public string FullName {get; set;}
public string FriendlyName {get; set;}
public List<ObjectInformation> Children {get;}
}
And I already have a method which flattens a root object into a simple list:
public static IEnumerable<ObjectInformation> Flatten(List<ObjectInformation> objs)
{
var localCopy = Helpers.General.DeepCopy(objs);
var finalList = new List<ObjectInformation>();
foreach(var obj in localCopy)
{
if(obj.Children.Count > 0)
{
finalList.AddRange(Flatten(obj.Children));
obj.Children.Clear();
}
obj.Parent = null;
finalList.Add(obj);
}
return finalList;
}
I know the above method could probably be improved, but it works right now. However, what I'm trying to do now is traverse the nested list and output the data, while indenting lines depending on the nesting level.
So, as an example, say the root object has two objects, the first one which has one child, and the second which has a child, which also has a child. I would want the output to be something like this:
FullName of Root Object 1
FullName of Child 1 of Root Object 1
FullName of Root Object 2
FullName of Child 1 of Root Object 2
FullName of Child 1 of Child 1 of Root Object 2
To do the indenting, I need some kind of counter to determine how deep the level is nested. I keep running into an issue using a recursive method though because on each call, the variable is reset. I was thinking, maybe I need to use a static variable to track the nesting level. The issue I see with that though is that as it travels back up, the static variable will still have the value of the deepest level its reached.
I'm kind of at a lost of how to proceed with this, albeit I'm sure it is probably a simple solution I just can't picture at the moment; I generally don't use/need recursion so I don't have a lot of experience actually using it.
Any help/suggestions you an provide would be greatly apprecated.
Thank You
I would recommend a method that takes the object and prints each with a defined space. Recursive call would add x amount of spaces each time you would go in deeper within the objects.
public static void Print(this ObjectInformation parent, int spacing = 2)
{
Console.WriteLine($"{new string(' ', spacing)}{parent.FullName}");
foreach (ObjectInformation child in parent.Children)
{
child.Print(spacing + 2);
}
}
I tried this same as an exercise and got a working result.
public void PrintInfoList(IEnumerable<ObjectInformation> list)
{
var result = string.Join("\n", list.Select(item => GetPrintedFormat(item)));
Console.WriteLine(result);
}
public string GetPrintedFormat(ObjectInformation info)
{
string printedFormat = string.Empty;
printedFormat = $"Fullname of {info.FriendlyName} - {info.FullName}";
if (info.Children != null && info.Children.Any())
{
childCount++;
_formatter = $"\n{string.Empty.PadRight(childCount, '\t')}";
printedFormat += $"{_formatter}{string.Join(_formatter, info.Children.Select(child => GetPrintedFormat(child)))}";
}
else
if (childCount > 0) childCount--;
return printedFormat;
}
this is tested working solution. Let me know what you think of this.
But i like to vote #Jawad's simplest way too. (clap)

For Each statement with a constructor list?

I am writing a pathfinding script in Unity, and I have been trying to figure out IEnumerables, and I feel I am not wording my internet searches correct, because it is not at all what I want.
I have 3 scripts, 1 find nodes and it adds to the OpenNodes list which is it's own script with constructor. I have An IEnumerable and it is still saying it can't access, or it needs to be static if just public. So my codes as follows:
public class OpenNodes: IEnumerator, IEnumerable {
public static Vector3 node;
public static int g;
public static IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}
public OpenNodes(Vector3 newNode, int newGscore)
{
node = newNode;
g = newGscore;
}
so what I am trying to do in a different "FinderScript" is along these lines:
foreach(Vector3 node in OpenNodes)
I have been trying my hardest to figure it out, but I have never worked in constructor lists like this. This is new to me, so any help, would be SO greatly appreciated.. Thanks
EDIT:
This for each needs to take node in as a vector3 as it is using .x,.y, and .z values for comparison and changes..
As you need to store a score per node you could simply use a Dictionary<Vector3, int> to store the values.
e.g.
var nodes = new Dictionary<Vector3, int>();
nodes.add(node, gScore);
And then either access them directly
nodes[node] = ...
Or loop through them
foreach (var node in nodes.Keys) {
...
}
Alternatively, perhaps you can create an object that maps the score and the node together (or simply use a Pair and then just use a list?
var nodes = new List<Vector3>();
var map = new Pair(node, gScore);
nodes.add(map);
Then loop through them as per usual:
foreach (var node in nodes) {
...
}
Not sure if i have understood you correctly, and i haven't run this code. HTH.
The problem I was facing was actually more simple that I realized. Below is the code of my constructor list:
public class OpenNodes {
public Vector3 node;
public int g;
public OpenNodes(Vector3 newNode, int newGscore)
{
node = newNode;
g = newGscore;
}
}
The code for the foreach is as follow:
foreach(OpenNodes list in OpenNode)
to populate the list looks like:
OpenNode.Add(new OpenNodes(nodeLocation, 0 ));
to remove an item looks like:
OpenNode.RemoveAll(OpenNodes => nodeLocation == OpenNodes.node && 0 == OpenNodes.g);
and to access my node it is simply:
list.node.x
list.node
list.node.z
ect.
This is iterating through each item in the list as list, and then you call upon each variable after that. I hope this code will help someone in the future!

Removing from linked list C#

I'm trying to delete a node, if x currently matches a int in my linked list.
I tried this, but once it removes the node it throws an error when examining foreach loop
public void DeleteNode(int x, LinkedList<name> myLinkedList) {
foreach (name item in myLinkedList) {
if (item.num.equals(x)) mylinkedList.Remove(x);
}
}
Hope that makes sense.
Yes, you can't iterate over a collection and modify it at the same time. However, LinkedList<T> lets you do the iteration explicitly pretty easily:
public void DeleteNode(int x, LinkedList<name> myLinkedList) {
var node = myLinkedList.First;
while (node != null) {
var nextNode = node.Next;
if (node.Value.num == x) {
myLinkedList.Remove(node);
}
node = nextNode;
}
}
Note that you can't get away with just taking node = node.Next; as the last line; the node is invalidated when it's removed.
This approach allows a single traversal of the list in O(n), and is likely to be the most efficient approach you'll find. It doesn't require any copying, or working with a collection (say List<T>) with less efficient removal complexity.
If you call remove during a foreach it will invalidate the enumerator, so this is not allowed.
Change your foreach to a simple for loop.
In this situation, I usually create a temporary collection and add it to it if it needs to be deleted. Then I loop through that list removing it from the original.
The way I write that, without invalidating the iterator, is:
foreach(var item in list.Where(w=>w.num.Equals(x)).ToArray())
list.Remove(item);
I remove Items from list in the following way:
for (int j = lst.Count - 1; j >= 0; j--)
{
var elem= lst[j];
lst.Remove(elem);
}
It looks very close to regular "foreach var elem in lst", which is the reason I like it.
I go from the end to the beginning cause otherwise you'll loose your indexing, and will need to track number of removed items.
info is a class.
This will find through linkedlist and delete the first item who's no property value is 1
LinkedList<info> infolist = new LinkedList<info>();
string todelete = "1";
info tmpitem = new info();
foreach (var item in infolist)
{
if (item.no == todelete)
tmpitem = item;
}
infolist.Remove(tmpitem);
public ListNode RemoveElements(ListNode head, int val)
{
if (head == null) return null;
head.next = RemoveElements(head.next, val);
return head.val == val ? head.next : head;
}

TreeNodeCollection reference problem

First off we have the almighty code!
List nodes = new List();
TreeNode Temp = new TreeNode();
TreeNodeCollection nodeList = treeViewTab4DirectoryTree.Nodes;
while (nodeList.Count != 0)
{
Temp = nodeList[0];
while (Temp.FirstNode != null)
{
Temp = Temp.FirstNode;
}
if (!nodes.Contains(Temp.FullPath))
{
nodes.Add(Temp.Text);
}
nodeList.Remove(Temp);
}
Now the problem: I have written the code above with the intent of creating a List containing the text from all the nodes in the tree. That works perfectly. The problem I am having is when I remove the nodes from my variable they are also being removed from the actual list. The question would be how can I make a copy of the list of nodes so I can play with them without messing with the ACTUAL list. How do I make a copy of it without just making a reference to it? Any help will be greatly appreciated!
Your problem arises because "nodeList" is a reference to treeViewTab4DirectoryTree.Nodes, rather than a copy of it.
The solution depends entirely on the what type of TreeNodeCollection you're using (WinForms, ASP.net, something else?), as you'll need to look for a .Copy(), .Clone(), .ToArray() method or similar to take a copy of the contents of the collection, rather than a reference to the existing collection.
If, for example, you're using asp.net and thus the System.Web.UI.WebControls.TreeNodeCollection, you could use the .CopyTo method in a way similar to this:
TreeNode[] x = null;
treeViewTab4DirectoryTree.Nodes.CopyTo(x, 0);
Updated to show stack based approach:
List<String> result = new List<String>();
Stack<IEnumerator> nodeColls = new Stack<IEnumerator>();
IEnumerator nodes = treeViewTab4DirectoryTree.Nodes.GetEnumerator();
nodeColls.Push(null);
while (nodes != null)
{
while (nodes.MoveNext())
{
result.add(nodes.Current.FullPath);
if (nodes.Current.FirstNode != null)
{
nodeColls.Push(nodes);
nodes = nodes.Current.Nodes.GetEnumerator();
}
}
nodes = nodeColls.Pop();
}
The code below does not work as was mentioned in comments, because it doesn't traverse the entire tree, but only takes the first leaf node of each top-level branch.
I actually thought the original code (in the question) did so too, because I thought the Remove would actually remove the top-level node after finding the first leaf node under it; but instead, it tries to remove the leaf node from the collection of top-level nodes, and just ignores it if it can't find it.
Original post, non-functioning code
First of all, why do you need to remove the items from your list?
List<string> nodes = new List<string>();
foreach (TreeNode tn in treeViewTab4DirectoryTree.Nodes)
{
TreeNode temp = tn;
while (Temp.FirstNode != null)
{
Temp = Temp.FirstNode;
}
if (!nodes.Contains(Temp.FullPath))
{
nodes.Add(Temp.Text);
}
}
To answer your concrete question, assuming the Nodes collection implements IEnumerable, use:
List<TreeNode> nodeList = new List<TreeNode>(treeViewTab4DirectoryTree.Nodes);
If you do decide to stick with your while loop, you can save an instatiation by changing
TreeNode Temp = new TreeNode();
to
TreeNode Temp = null;
... you're never actually using the object you create, at least in the part of the code you've shown.

Categories

Resources