I want to build a tree structure like this:
root Id 1
child id 2
grandChild id 3
Code sample below. If I use GetChildrenNodesCorrect(), I get the correct result. But when GetChildrenNodesWrong() is used, it returns below:
root Id 1
child id 2
Null
I know that ToList() is not deferred execution, and returns result immediatelly. Could anyone explain this?
public class ToListTest
{
public static void Entry()
{
var toListTest = new ToListTest();
toListTest.Test();
}
public void Test()
{
List<Node> newsList = new List<Node>
{
new Node{Id = 1, ParentId = 0},
new Node{Id = 2, ParentId = 1},
new Node{Id = 3, ParentId = 2}
};
var root = BuildUpTree(newsList);
}
private TreeNode BuildUpTree(List<Node> newsList)
{
var root = new TreeNode { currentNode = newsList.First(n => n.ParentId == 0) };
BuildUpTreeChildrenNodes(newsList, root);
return root;
}
private void BuildUpTreeChildrenNodes(List<Node> newsList, TreeNode currentTreeNode)
{
currentTreeNode.Children = GetChildrenNodesWrong(newsList, currentTreeNode);
foreach (var node in currentTreeNode.Children)
{
BuildUpTreeChildrenNodes(newsList, node);
}
}
private IEnumerable<TreeNode> GetChildrenNodesWrong(List<Node> newsList, TreeNode cuurentNode)
{
return newsList.Where(n => n.ParentId == cuurentNode.currentNode.Id)
.Select(n => new TreeNode
{
currentNode = n
});
}
private IEnumerable<TreeNode> GetChildrenNodesCorrect(List<Node> newsList, TreeNode cuurentNode)
{
return GetChildrenNodesWrong(newsList, cuurentNode).ToList();
}
public class TreeNode
{
public Node currentNode { get; set; }
public IEnumerable<TreeNode> Children { get; set; }
}
public class Node
{
public int Id { get; set; }
public int ParentId { get; set; }
}
}
Update
In debug, when using GetChildrenNodesWrong(), root has both child and grandchild before the method returns. After the method returns, root has only child, and grandchild is null.
Update 2
IMO, the problem might not be related to clean code. But anyone is welcome to show more intuitive code.
Every time the IEnumerable is evaluated the Linq query is re-executed. So, when you're computing the tree, it is allocating space for nodes but not assigning them to any permanent variable. This means that in the foreach loop in BuildUpTreeChildrenNodes, you are not calling the recursive function on the instance of the node you want. Instead, you're calling it on a re-instantiated version of the node that has been created by the foreach loop (which enumerates the IEnumerable). When you call ToList on the IEnumerable instead, then the foreach loop will return the elements of the list, which is in memory.
If you make root public static, and then debug your code, you'll see that when you call BuildUpTreeChildrenNodes, the node argument is not the instance of the node that you want. Even though it has the same ID and represents the same node in the graph, it is not actually connected in any way to the root node. Check:
root.Children.Any(n => n.Id == node.Id) //true
root.Children.Contains(node) //false
The simplest way to see your problem is here:
//Create a singleton Node list:
var nodeSingleton= Enumerable.Range(0, 1).Select(x => new Node { Id = x });
Console.Write(nodeSingleton.Single() == nodeSingleton.Single());
You might expect this to return true, but in fact it will be false - both times the Single Linq method is called, the deferred execution of the singleton variable is re-evaluated, and returns a different instance of the Node class.
If you call ToList on the singleton, however, then you get the list in memory and the Single method will return the same instance of the Node.
More broadly, I think the problem with this code is that it mixes up imperative and functional code too much. It is strange that so many of the methods are void and then the GetChildrenNodesWrong method is not. I think you should pick a style and stick with it, since switching paradigms can be confusing.
I'm not entirely sure what you're asking. All LINQ queries have deferred execution, when you call ToList() you're simply forcing the query to execute. I think the main problem is in your where clause. Only two objects satisfy the condition so the IEnumerable returned by your LINQ query should only have 2 objects.
It's not doing what you expect because the LINQ query in GetChildrenNodesWrong is producing an "off by one" error. Here is basically what happens;
1) we feed it root for n = root nothing happens. We move to the next node.
2) n.Id = 1, the where condition is met by node 2 as it's parentId is 1. We allocate a new node, point current to node 2
3) We get to the third node now. n.ParentId = 2 and current.Id = 2. We have a match so we allocated another node and point current to node 3.
4) we're at the end of the list. Grand child is never allocated because we're off by one.
Basically you iterate x time where x is the length of the list but because current = n on the first iteration you don't allocate a node so you end up with x -1 nodes when you're expecting x.
Related
Hello I currently have a TreeView with the following structure:
Root
Child
Root
Child
Root
Child
Child
RootN
ChildN
The TreeView structure can basically have NRootNodes - NChildren and the NRootNodes can have NRoots and NChildren so basically just like Windows Explorer Window.
My current issue that I have is that I have to get all the Parents or Root, in this case Roots / RootN and then I have to Remove all of their Child Nodes, in this case Child / ChildN. In the end I have to have only the Parent Nodes and then Clone them so I can move them to a different location within the TreeView.
RootNodes have a unique Tag - Folder and ChildNodes have another unique Tag - Calculations, as I have said earlier, I have to get rid of all Calculations in the Selected Node so only the Structure of that Selected Node will Remain.
Basically in the end I have to have something like this:
Root
Root
Root
Root
Root
I have a recursive method that "scans" the SelectedNode and gets all the Parents:
public List<TreeNode> CollectParentNodes(TreeNodeCollection parentCollection, List<TreeNode> collectedNodes)
{
foreach (TreeNode node in parentCollection)
{
if (!collectedNodes.Contains(node.Parent))
{
collectedNodes.Add(node.Parent);
parentNodeAdded = true;
}
if (node.Level != 0 && node.Tag.ToString() != Enumerations.NodeType.Calculation.ToString())
collectedNodes.Add(node);
if (node.Nodes.Count > 0)
CollectParentNodes(node.Nodes, collectedNodes);
}
parentNodeAdded = false;
return collectedNodes;
}
In the end I have a List that will hold all the Parents but the problem I'm facing is that that Parents also contain their descendents, in this case the Calculations
I have searched Google and StackOverFlow but I could not find anything of help, I appologize in advance if this has already been answered.
Thank you.
You can create an extension method GetAllNodes for TreeView that return List
Remember using using System.Linq; at top of your code
public static class Extensions
{
public static List<TreeNode> GetAllNodes(this TreeView tree)
{
var firstLevelNodes = tree.Nodes.Cast<TreeNode>();
return firstLevelNodes.SelectMany(x => GetNodes(x)).Concat(firstLevelNodes).ToList();
}
private static IEnumerable<TreeNode> GetNodes(TreeNode node)
{
var nodes = node.Nodes.Cast<TreeNode>();
return nodes.SelectMany(x => GetNodes(x)).Concat(nodes);
}
}
And the usage will be:
var result = this.treeView1.GetAllNodes().Where(x => x.Tag == "FOLDER").ToList();
Remember to add namespace of your extensions class at top of your code wherever you want to use it.
As an example you can set All nodes with tag of Folder to be in Red forecolor:
var result = this.treeView1.GetAllNodes().Where(x => (x.Tag as string) == "FOLDER").ToList();
result.ForEach(x => x.ForeColor = Color.Red);
And here is an Screenshot
This will create a new tree with the selected node as root and which child nodes consists only of nodes that are tagged "Folder".
You need to create a copy constructor (or extension method) to deep copy the node to prevent the manipulation on the node objects to impact your original tree source:
public TreeNode CollectFolderChildNodes(TreeNode selectedNode)
{
if (selectedNode.Tag == "Calculation")
return null;
// Get all the children that are tagged as folder
var childRootNodes = selectedNode.Children.Where((childNode) => childNode.Tag == "Folder";
// Clone root node using a copy constructor
var newRoot = new TreeNode(selectedNode);
newRoot.Children.Clear();
foreach (var childNode in childRootNodes)
{
// Iterate over all children and add them to the new tree
if (childNode.Children.Any())
{
// Repeat steps for the children of the current child.
// Recursion stops when the leaf is reached
newRoot.Children.Add(CollectFolderChildNodes(childNode));
}
else
{
// The current child item is leaf (no children)
newRoot.Children.Add(new TreeNode(childNode));
}
}
return newRoot;
}
I think this should do it, but I didn't tested it. But maybe at least the idea behind it is clear.
But as I mentioned before, maybe it's better to traverse the tree (using same ItemsSource) and set a property (e.g. IsHidingCalculations) to true so that only the folders will show up. You would need to implement an ItemsStyle and use a trigger that sets the items Visibility to Collapsed when your IsHidingCalculations evaluates to true.
To clone a node without its children you can create an extension method like this:
public static TreeNode CloneWithoutChildren(this TreeNode node)
{
return new TreeNode(node.Text, node.ImageIndex, node.SelectedImageIndex)
{
Name = node.Name,
ToolTipText = node.ToolTipText,
Tag = node.Tag,
Checked = node.Checked
}
}
and then:
collectedNodes.Add(node.CloneWithoutChildren());
Inside the class Test, there must be a public method getLinkedList and it shall receive nothing, but answer with a Node object.
You must create the Node class inside the Test class and it must be public. The Node class must have two instance variables. One that holds numbers and have the name TAL and one that holds a Node object and have the name NEXT.
Calling the getLinkedList the first time, it must answer with a Node object that has the TAL=1 and the NEXT=null
Calling the getLinkedList the second time, it must answer with a Node object that has the TAL=2 and the NEXT points to a Node with the TAL=1. This means that the first Node is placed last in the list and the newest Node is always at the front and its TAL value is the same as the numbers of Nodes in the list.
This is what I've done so far. Don't give me the answer direct please.
public Node getLinkedList()
{
Node n = new Node();
n.TAL = 1;
n.NEXT = null;
Node n1 = new Node();
n1.TAL = 2;
n.NEXT.TAL = n.TAL;
}
public class Node
{
public int TAL;
public Node NEXT;
}
Alright without giving you too much, here's what you need to do with your get linked list function.
public Node GetLinkedList()
{
//Check if your linked list is empty
//If yes return your TAL = 1 node
//If no create a new node with new TAL and return that
}
This should get you pointed in the right direction at least. You need some conditional logic in there
I have a recursion function that builds a node list from an IEnumerable of about 2000 records. The procedure currently takes around 9 seconds to complete and has become a major performance issue. The function serves to:
a) sort the nodes hierarchically
b) calculate the depth of each node
This is a stripped down example:
public class Node
{
public string Id { get; set; }
public string ParentId { get; set; }
public int Depth { get; set; }
}
private void GetSortedList()
{
// next line pulls the nodes from the DB, not included here to simplify the example
IEnumerable<Node> ie = GetNodes();
var l = new List<Node>();
foreach (Node n in ie)
{
if (string.IsNullOrWhiteSpace(n.ParentId))
{
n.Depth = 1;
l.Add(n);
AddChildNodes(n, l, ie);
}
}
}
private void AddChildNodes(Node parent, List<Node> newNodeList, IEnumerable<Node> ie)
{
foreach (Node n in ie)
{
if (!string.IsNullOrWhiteSpace(n.ParentId) && n.ParentId == parent.Id)
{
n.Depth = parent.Depth + 1;
newNodeList.Add(n);
AddChildNodes(n, newNodeList, ie);
}
}
}
What would be the best way to rewrite this to maximize performance? I've experimented with the yield keyword but I'm not sure that will get me the result I am looking for. I've also read about using a stack but none of the examples I have found use parent IDs (they use child node lists instead), so I am a little confused on how to approach it.
Recursion is not what is causing your performance problem. The real problem is that on each recursive call to AddChildNodes, you traverse the entire list to find the children of the current parent, so your algorithm ends up being O(n^2).
To get around this, you can create a dictionary that, for each node Id, gives a list of all its children. This can be done in a single pass of the list. Then, you can start with the root Id ("") and recursively visit each of its children (i.e. a "depth first traversal"). This will visit each node exactly once. So the entire algorithm is O(n). Code is shown below.
After calling GetSortedList, the sorted result is in result. Note that you could make children and result local variables in GetSortedList and pass them as parameters to DepthFirstTraversal, if you prefer. But that unnecessarily slows down the recursive calls, since those two parameters would always have the same values on each recursive call.
You can get rid of the recursion using stacks, but the performance gain would probably not be worth it.
Dictionary<string, List<Node>> children = null;
List<Node> result = null;
private void GetSortedList()
{
var ie = data;
children = new Dictionary<string,List<Node>>();
// construct the dictionary
foreach (var n in ie)
{
if (!children.ContainsKey(n.ParentId))
{
children[n.ParentId] = new List<Node>();
}
children[n.ParentId].Add(n);
}
// Depth first traversal
result = new List<Node>();
DepthFirstTraversal("", 1);
if (result.Count() != ie.Count())
{
// If there are cycles, some nodes cannot be reached from the root,
// and therefore will not be contained in the result.
throw new Exception("Original list of nodes contains cycles");
}
}
private void DepthFirstTraversal(string parentId, int depth)
{
if (children.ContainsKey(parentId))
{
foreach (var child in children[parentId])
{
child.Depth = depth;
result.Add(child);
DepthFirstTraversal(child.Id, depth + 1);
}
}
}
I'm trying to populate hierarchical data in a .NET 2.0 (yes, 2.0) application and upgrading right now is off the table (so no LINQ, LINQ Bridge, or other things).
I was wondering if there is a better way to populate hierarchical data into this class structure? I'm pretty sure there is a far better way for this to be accomplished.
It would be really nice to see a good way to do this. If anyone has the time to show a .NET 2.0 way and if there is a different way they would do it in .NET 4.0+ that would be great.
Here is an example of the node type structure:
using System.Collections.Generic;
public class ExampleNode
{
private int _id;
private Nullable<int> _parentId;
private int _depth;
private List<ExampleNode> _children = new List<ExampleNode>();
public ExampleNode()
{
}
public virtual int ApplicationNumber {
get { return _id; }
set { _id = value; }
}
public virtual Nullable<int> ParentId {
get { return _parentId; }
set { _parentId = value; }
}
public virtual int Depth {
get { return _depth; }
set { _depth = value; }
}
public virtual List<ExampleNode> Children {
get { return _children; }
set { _children = value; }
}
}
Here is an example function that is being utilized to populate the node structure. It seems like it is not the best way to do this and it has the potential not to populate grandchildren type data. Depth comes back from the stored proc as the level in the hierarchy (items with a level of 0 are top level, if a node is the child of a top level node it is at level 1, a grandchild of a top level node is level 2, etc.)
public List<ExampleNode> GetNodes()
{
// This may not be optimal.
List<ExampleNode> nodeList = new List<ExampleNode>();
Dictionary<int, ExampleNode> nodeDictionary = new Dictionary<int, ExampleNode>();
using (SqlDataReader reader = SqlHelper.ExecuteReader(ConfigurationManager.ConnectionStrings("SqlServer").ConnectionString, CommandType.StoredProcedure, "proc_GetNodeStructure", new SqlParameter("#UserId", userId), new SqlParameter("#NodeTypeId", nodeType))) {
while (reader.Read) {
ExampleNode nodeInstance = new ExampleNode();
nodeInstance.Id = Convert.ToInt32(reader("Id"));
nodeInstance.Depth = Convert.ToInt32(reader("Depth"));
if (!Information.IsDBNull(reader("ParentId"))) {
nodeInstance.ParentId = Convert.ToInt64(reader("ParentId"));
}
// Add to list
nodeList.Add(nodeInstance);
// Add to dictionary
nodeDictionary.Add(nodeInstance.Id, nodeInstance);
}
}
foreach (ExampleNode item in nodeList) {
if (item.ParentId.HasValue) {
nodeDictionary(item.ParentId).Children.Add(item);
}
}
for (int i = nodeList.Count - 1; i >= 0; i += -1) {
if (nodeList(i).Depth > 0) {
nodeList.RemoveAt(i);
}
}
return nodeList;
}
If I understand correctly, you
gather the nodes into a list and a dictionary
iterate through the list and arrange parent/child relationships via the dictionary
remove nodes from the list that have a positive depth
... which leaves the list containing the top-most nodes in the hierarchical stucture. Your algorithm seems correct to me.
The first two operations are O(n) complexity in time and space with respect to the number of nodes, which is pretty good!
The only real inefficient thing you are doing is removing elements from the list in step 3. Because the underlying storage is a vector, removing an element from the front of the list is expensive, because all of the remaining elements need to be copied down. You are trying to minimize the amount of such copying by iterating over the list backward. Imagine that the last half of the list is parent nodes, and the front half is child nodes. Whenever you remove a child node, you will still have to copy half the original list size every time a child node is removed. This approaches O(n^2) behaviour.
So for step 3 you have at least two choices if you wish to improve performance in time:
Make a second list that contains only the elements from the first where the depth == 0.
Use a linked list instead, so that deletions are O(1) instead of up to O(n) performance.
Here is the code for the first option:
...
List<ExampleNode> roots = new List<ExampleNode>();
for (int i = 0; i < nodeList.Count; i ++) {
if (nodeList[i].Depth == 0) {
roots.Add(nodeList[i]);
}
}
return roots;
You could potentially save even a little more time by counting how many root nodes there are during step 1 or 2, and then initializing the second list so its capacity is equal to the number of root nodes. This will prevent unnecessary allocations and copying of the underlying list vector while you are adding elements to the list.
List<ExampleNode> roots = new List<ExampleNode>(rootCount);
The same applies to the first nodeList; you can delay its construction until you know the number of records returned from the query.
What about using NHibernate? It works with .net 2 plus, so you can move forward with it as well.
I'm trying to work out how to construct a tree view where child nodes are based on a single database table in which rows can link to other rows indicating the parent/child relationship.
For example, given the table:
ID ID_parent Ten
1 null a
2 1 b
3 1 c
4 null d
5 4 e
6 4 f
I want show follow:
a
b
c
d
e
f
Without knowing your data retrieval technology or how you are going to data bind to your component I will be a generic as possible.
First thing you need to do is get the data out of the database. There are ways to do this hierarchically in sql server but in the spirit of being generic lets assume you are going to get it out as a flat structure. The important thing is your data is ordered by it's parent id, you can do this in sql or in code.
Assuming that you now have an ordered set of data we can populate our node objects. An example of this would be:
public class Node : ICollection<Node>
{
private List<Node> childNodes;
public Node()
{
childNodes = new List<Node>();
}
public Node this[int index]
{
get { return childNodes[index]; }
set { childNodes[index] = value; }
}
public void Add(Node childNode)
{
childNodes.Add(childNode);
}
/* Rest of ICollection<T> implementation */
}
Now to populate the structure you need to iterate through the original data.
public Node PopulateTree(TreeData[] treeData)
{
Dictionary<{IdType}, Node> flattenedTree = new Dictionary<{IdType}, Node>();
foreach(TreeData data in treeData)
{
Node node = new Node();
if (data.ParentId != {EmptyId})
{
Node parentNode = flattenedTree[data.ParentId];
parentNode.Add(node);
}
flattenedTree.Add(data.Id, node);
}
}
I have not tested the pseudo code but it should show you a way of getting flattened data into a hierarchical structure. You should be able to refactor this down to a cleaner structure but how depends on your code base.