I'm having trouble trying to delete a item inside a tree structured object.
My object is as below
TreeNode
{
string name;
ObservableCollection<TreeNode> Children;
}
I thought if I recursively process through the tree and find my node and delete it but I ran into trouble.
I did something along the lines of
Updated:
DeleteNode(ObservableCollection<TreeNode> children, TreeNode nodetodelete)
{
if(children.remove(nodetodelete))
{
return;
}
else
{
foreach(var child in children)
{
DeleteNode(child, nodetodelete);
}
}
}
I realize while I was writing the code that I would eventually run into manipulation exception while iterating through a collection that has a chance of being changed.
I could build a giant change of for loops since I know exactly the max deep length(which I did for a place holder) but that seems really bad. . . .
Can anyone point me in a better general direction. I kind of wonder if my data structure is the cause of this.
Update:
This will look awful and kinda of code smell but I got the recursion to "work"
by throw a exception when I find my node.
DeleteNode(children, nodetodelete)
{
if(children.remove(nodetodelete)
{
throw FoundException();
}
else
{
foreach(var child in children)
{
DeleteNode(child, nodetodelete)
}
}
}
Is there any other way of breaking out of a recursion.
I would deal with this by making a small change to my design (assuming the snippet in your question is pseudocode for a class):
TreeNode
{
string name;
TreeNode Parent;
ObservableCollection<TreeNode> Children;
public void Delete()
{
Parent.Children.Remove(this);
}
}
This makes a little bit more work for you maintaining an extra reference when manipulating your object graph, but saves you a lot of effort and code when doing things like deletes as you can see above.
You haven't shown how you're constructing TreeNodes, but I'd make the parent and a collection for the children arguments of the constructor.
You can safely iterate over the collection of children nodes and remove them, as long as you don't change the original collection. This can be done by creating an array of the collection and iterating over that instead.
DeleteNode(ObservableCollection<TreeNode> children, TreeNode nodetodelete)
{
if (children.remove(nodetodelete))
{
return;
}
else
{
foreach (var child in children.ToArray())
{
// If anything is deleted in the collection, it will not break the iteration here, as we are iterating over an Array and not "children"
DeleteNode(child, nodetodelete);
}
}
}
This will create a new collection for you to iterate over. If a child node is deleted from children, the foreach loop will not throw an exception. That is because the original collection was changed, while we iterate over a secondary collection.
Related
I have a TreeView with several nodes and if a special node (you will see in code) is deleted, the parent node should be expanded after updating the TreeView.
Here's how I tried it:
public void Remove(){
...
...
else if ((NodeType)n.Tag == NodeType.Attribute) //Here I simply check if it's the "special" parent
{
Commands.CommandAttributeRemove cmd = (Commands.CommandAttributeRemove)mAppData.CommandFactory.Create("AttributeRemove");
cmd.Data = n.Text;
cmd.ObjectClass = mObjectClass;
cmd.ObjectTypeName = n.Parent.Parent.Text;
list.Add(cmd);
mNodeToExpand = mTreeView.SelectedNode.Parent; //THIS LINE IS IMPORTANT... mNodeToExpand is a member variable which I use in UpdateData()
}
...
...
UpdateData();
}
public void UpdateData()
{
… //A lot of not so important stuff happening here (at least not important for what I want, I think)
...
//Update Selected Items (for the case that objects were deleted) and UpdateSelection
OnSelect();
//UpdateSelection();
this.Update();
Now that's interesting stuff:
if (mNodeToExpand != null)
{
mNodeToExpand.Expand();
mNodeToExpand = null;
}
}
This is how I tried to achieve what I want, but the node doesn't expand (it still has other children).
In the Remove() I also tried mTreeView.SelectedNode.Parent.Nodes.Add(new Node("Blabla")); but it doesn't even add a node.
And in the if(mNodeToExpand!=null) I also try to set the selectedNode to mNodeToExpand, but it gives me a NullReferenceException EVEN THOUGH I CHECK IF IT'S NULL IN THE IF. WHY?
This can't freaking be it. FullPath is apparently the path of the node in the treeview, so for example if you have Node2 which is a sub-node of Node1, then the FullPath of Node1 is "Node1" and of Node2 "Node1//Node2" … Now the thing is that FOR SOME WEIRD REASON my mNodeToExpand loses the Information of FullPath and suddenly it's only "Node2" instead of "Node1//Node2"... Because of that it isn't expanded cause the TreeView doesn't find a node with this FullPath… Now what I did is that I stored the FullPath in a string and when I want to expand it I search for the node with this FullPath and then expand what is returned. This is so stupid, cause imagine a list of 1 000 000 nodes. This could take very long. I'm starting to really dislike this Crownwoods.DotNetMagic library but it's already existing Code I'm working on.
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.
I want to clone a tree of objects, where every node also references the parent node:
How do I avoid cloning parents more than once at a time? Every node is a Guy. When I clone the root guy who has no parent, I clone its children and children's children. The children's parent mustn't be cloned a second time because I've cloned it already. So what cloning technique do I use? And how does it avoid my dilemma?
public class Guy
{
private Guy parent = null;
private List<Guy> children = new List<Guy>();
private double x, y, z;
}
Thanks for any useful advise!
(If the question is duplicate and you let me know, I will delete it, no problem.)
To my understanding, the cloning can be done with the following recursive function.
public Guy Clone(Guy original, Guy parent = null)
{
Guy originalClone = new Guy();
originalClone.parent = parent;
foreach (Guy child in original.children)
{
originalClone.children.Add(Clone(child,originalClone));
}
return originalClone;
}
A root to the copy of the tree is returned by the following call.
Guy RootOfCopy = Clone(RootOfOriginal);
I'm having problems with traversing all descendants of an object.
The 'unit' in the code below is of type Unit in my program. It has a property ChildUnits which returns a List<Unit> of the children of the unit.
I can successfully perform operations on the children. Then I check if those children have children, and if they do I can perform operations on them as well.
However, I need to check all descendants in case there is more depth than just grandchildren. I had a go with while loops in addition to the code below but it got really messy so I left it out.
This is the code I have reverted back to:
foreach (var child in unit.ChildUnits)
{
//do something here with the child (I know it sounds dodgy).
bool hasMoreChildren = child.ChildUnits.Count != 0;
if(hasMoreChildren)
{
foreach (var descendant in child.ChildUnits)
{
//do something here with the descendant.
}
}
}
I could just go another level deep as it's relatively rare for a unit to have more depth than that. But that's not a clean solution.
I think I might need to use a graph traversal algorithm and/or recursion perhaps, but I would like some advice on how to solve this problem most efficiently.
Edit: Is it possible to do this without defining a new function/method?
Edit: Is it possible to do this without defining a new function/method?
You could use an anonymous method...which is not exactly "not defining a new method", I know :)
However, there's another issue you should take care of: Circular references... even if you dont think there will be any
Here's an implementation, without defining any new method
Action<IEnumerable<Unit>> process = null;
var processed = new HashSet<Unit>();
process = list => {
foreach(var u in list.Where (processed.Add))
{
// do something here with u
//... and then process children
process(u.ChildUnits);
}
};
process(myList); // do the actual processing
Algorithm like this:
def traverse(Unit i):
for (Unit child : i.childList):
// Perform your logic for child
traverse(child)
This will perform the same function for each child for the first node , and when applying it for i.child[j] it will perform the same function for all i.child[j].child[k] so it will perform what you want for each node and all its childs.
instead you can use stack :
stack s;
s.push(firstNode);
while(!stack.empty()):
t = stack.pop()
foreach(Unit child : t):
s.push(child)
// Perform logic for child
You can use recursion:
void processChildren(List<Unit> children)
{
foreach (var child in children)
{
//do something here with the child (I know it sounds dodgy).
processChildren(child.Children); // recursive call here
}
}
If you don't want to define a new method, you could also roll your own stack:
var stack = new Stack<Unit>();
stack.push(firstUnit);
while( !stack.Any() ) {
var item = stack.pop();
//do something here with the item
foreach(var child in item.Children)
{
stack.push(child);
}
}
Another way to do this without recursion or an action/lambda, is to use a list with items to handle.
Like this.
var toDoList = new List<Unit> { unit };
while (toDoList.Any()) {
// Get current child, and remove it from the to-do-list
var currentChild = toDoList.First();
toDoList.RemoveAt(0);
// Do something with the current child.
// ...
// Now see, if the child has any children to handle
if (currentChild.ChildUnits != null && currentChild.ChildUnits.Any()) {
toDoList.AddRange(currentChild.ChildUnits);
}
}
I have the following recursive method which takes the an XHTML document and marks nodes based on certain conditions and It is called like below for a number of HTML contents:-
XmlDocument document = new XmlDocument();
document.LoadXml(xmlAsString);
PrepNodesForDeletion(document.DocumentElement, document.DocumentElement);
The method definition is below
/// <summary>
/// Recursive function to identify and mark all unnecessary nodes so that they can be removed from the document.
/// </summary>
/// <param name="nodeToCompareAgainst">The node that we are recursively comparing all of its descendant nodes against</param>
/// <param name="nodeInQuestion">The node whose children we are comparing against the "nodeToCompareAgainst" node</param>
static void PrepNodesForDeletion(XmlNode nodeToCompareAgainst, XmlNode nodeInQuestion)
{
if (infinityIndex++ > 100000)
{
throw;
}
foreach (XmlNode childNode in nodeInQuestion.ChildNodes)
{
// make sure we compare all of the childNodes descendants to the nodeToCompareAgainst
PrepNodesForDeletion(nodeToCompareAgainst, childNode);
if (AreNamesSame(nodeToCompareAgainst, childNode) && AllAttributesPresent(nodeToCompareAgainst, childNode))
{
// the function AnyAttributesWithDifferingValues assumes that all attributes are present between the two nodes
if (AnyAttributesWithDifferingValues(nodeToCompareAgainst, childNode) && InnerTextIsSame(nodeToCompareAgainst, childNode))
{
MarkNodeForDeletion(nodeToCompareAgainst);
}
else if (!AnyAttributesWithDifferingValues(nodeToCompareAgainst, childNode))
{
MarkNodeForDeletion(childNode);
}
}
// make sure we compare all of the childNodes descendants to the childNode
PrepNodesForDeletion(childNode, childNode);
}
}
And then the following method which would delete the marked node:-
static void RemoveMarkedNodes(XmlDocument document)
{
// in order for us to make sure we remove everything we meant to remove, we need to do this in a while loop
// for instance, if the original xml is = <a><a><b><a/></b></a><a/></a>
// this should result in the xml being passed into this function as:
// <a><b><a DeleteNode="TRUE" /></b><a DeleteNode="TRUE"><b><a DeleteNode="TRUE" /></b></a><a DeleteNode="TRUE" /></a>
// then this function (without the while) will not delete the last <a/>, even though it is marked for deletion
// if we incorporate a while loop, then we can insure all nodes marked for deletion are removed
// TODO: understand the reason for this -- see http://groups.google.com/group/microsoft.public.dotnet.xml/browse_thread/thread/25df058a4efb5698/7dd0a8b71739216c?lnk=st&q=xmlnode+removechild+recursive&rnum=2&hl=en#7dd0a8b71739216c
XmlNodeList nodesToDelete = document.SelectNodes("//*[#DeleteNode='TRUE']");
while (nodesToDelete.Count > 0)
{
foreach (XmlNode nodeToDelete in nodesToDelete)
{
nodeToDelete.ParentNode.RemoveChild(nodeToDelete);
}
nodesToDelete = document.SelectNodes("//*[#DeleteNode='TRUE']");
}
}
When I use the PrepNodesForDeletion method without the infinityIndex counter, I get OutOfMemoryException for few HTML contents. However, If I use infinityIndex counter, It may not be deleting nodes for some HTML contents.
Could anybody suggest any way to remove recursion. Also I am not familiar with the HtmlAgility pack. So, If this can be done using that, could somebody provide some code sample.
Well, if I understand your algorithm correctly you want to do this:
For each node in the tree compare it against all its child nodes in a non-recursive fashion, correct?
// walk the tree in DFS
public void XmlTreeWalk(XmlNode root, Action<XmlNode, XmlNode> action)
{
var nodesToCompare = new Stack<XmlNode>();
foreach (XmlNode child in root.ChildNodes)
{
nodesToCompare.Push(child);
}
while (nodesToCompare.Count > 0)
{
var top = nodesToCompare.Pop();
action(root, top);
foreach (XmlNode child in top.ChildNodes)
{
nodesToCompare.Push(child);
}
}
}
// for each node: prepare all its children for deletion
public void PrepareForDeletion(XmlNode root)
{
XmlTreeWalk(root, (r, c) => PrepareSubtreeForDeletion(r, c));
}
// for each node, compare all its children against the toCompare node
private void PrepareSubtreeForDeletion(XmlNode toCompare, XmlNode root)
{
XmlTreeWalk(root, (unused, current) => MarkNodeForDeletion(toCompare, current));
}
// your delete logic
public void MarkNodeForDeletion(XmlNode toCompare, XmlNode toCompareAgains)
{
...
}
What this should do is: Walk the tree top to bottom and for each node walk the subtree of that node comparing all children against this node.
I haven't tested it so it might contain bugs but the idea should be clear. Apparently this algorithm is O(n^2).
To remove recursion, the childs and parents must know about each other.
Then you can traverse say down the right leg from the root parent, until you reach the right most bottom leg.
And then from there, go up one, then down left one, and then down right until bottom. Repeat up one, down left, and then right as far as possible, etc. until you have looped over the entire tree structure.
I'm not sure on what you are attempting to do, to suggest how to use this method on your problem.
Your problem is that you have badly formed XML and as a direct result your DOM is a mess. What I think you are going to have to do is to use a SAX parser (which must exist for .net) and implement the logic to fix the DOM yourself which appears to be what you re trying to do.
This method isn't recursive but is going to require you to do some work that you didn't realize that you needed to do.
also note that you are getting an out of memory exception and not a stack overflow exception which reinforces the idea that too much recursion is not your problem per se.