How to traverse a json tree file and remove nodes? - javascript

I'm trying to traverse through a json file (a tree) and given a specific node, I want to keep THAT node and all its children. I have tried writing some javascript code that does this, but I get an error "Uncaught RangeError: Maximum call stack size exceeded". Here's the code I use:
function removeNodes(tree){
//Check if root node contains any children.
if(tree["children"]){
var children = tree["children"];
//Loop through all children and delete their everything further down in their hierarchy.
for(var i = 0; i < children.length; i++) {
var node = children[i];
var grandChildren = node["children"];
if(grandChildren){
grandChildren.splice(i,1);
i--;
}
removeNodes(node);
}
}
}
What am I doing wrong here? And how can I traverse my json file properly. To explain once more:
Given a root node ("tree" in this case), I want to keep the root node and all it's children, but remove anything else below in the hiercharchy.
Thanks in advance!

function removeNodes(tree, desiredNode){
//check if node is one we want
if (tree == desiredNode) {
return tree;
}
//Check if root node contains any children.
if(tree && tree.children){
var children = tree.children;
//Loop through all children and delete their everything further down in their hierarchy.
for(var i = 0; i < children.length; i++) {
var node = removeNodes(children[i], desiredNode);
if (node == desiredNode) {
return node;
}
}
}
return false; //if no match found
}
var foo = removeNodes(tree, someNodeToKeep);
if (foo) {
var parent = tree.parentNode;
parent.removeChild(tree);
parent.appendChild(foo); //parent now contains only that node and children
}
Please note that like any recursive function, this can blow the stack on languages without proper tail calls, but unless you're using this to search a large data structure it should be fine.

Related

How to implement the search for all paths inside a directed graph in JavaScript?

I would like to find all distinct paths without cycles in following graph:
From this graph, i composed the adjacency list, starting from node 0 and going to the right (in the picture above):
var rightAdjacent = [[1,7],[2],[3,9],[4],[5],[6,10],[8],[2],[5],[11],[12]];
As i am a noob in graphs, i started from a canonical algorithm for BFS, which seems to me the cheapest way to get what i need:
...
var paths = []
queue.push(0); // Starting node
parents[0] = null;
while (queue.length > 0) {
u = queue.splice(0, 1)[0];
for (i = 0, l= rightAdjacent.length; i < l; i++) { // Explore edge(u, v).
v = rightAdjacent[u][i];
if (rightAdjacent[v]) {
if(rightAdjacent[v].status === 'unexplored') {
rightAdjacent[v].status = 'exploring'; // Node u has been discovered
queue.push(v);
parents[v] = u;
}
} else {
paths.push(collectPath(parents));
}
}
rightAdjacent[u].status = 'explored';
}
... but in my first attempt i was able to collect only the list of the connected components, after which and until now, only garbage.
I have also composed the left-adjacency list, not knowing if this could be useful to speed up the search or to back-trace the discovered path. Any clue about this?
For the graph in the picture, i'm expecting following result (please correct me if i'am wrong):
[0,1,2,3,4,5,6],
[0,1,2,9,5,6],
[0,1,2,9,5,10,11,12],
[0,7,8,2,3,4,5,6],
[0,7,8,2,9,5,10,11,12]
where each single path has the nodes ordered from the starting node, through the first encountered, until the last.
Starting from an adjacency list, and without using recursion, wouldn't it be this the simplest way to collect all this paths, (the ordered parent nodes of all the parent paths) in my example, starting from node 0 and ending at nodes 6 and 12?
What is wrong in this piece of code?
Your rightAdjacent is missing the neighbours of node 6, which should be an empty array. An easy solution to implement is to do bfs but deep copy the visited and current path when you add a node to your path. When you have no neightbours, you can output/save the current path
var rightAdjacent = [[1,7],[2],[3,9],[4],[5],[6,10],[],[8],[2],[5],[11],[12]];
var queue = [{visited:{0:true},path:[0]}] // start with node 0
function bfs(){
while(queue.length){
obj = queue.pop() // get last added path
node = obj.path[obj.path.length-1] // get last visited node from that path
visited = obj.visited
if(node >= rightAdjacent.length || rightAdjacent[node].length == 0){ // we reached a leaf node
console.log(obj.path)
}else{
for(var i=0;i<rightAdjacent[node].length;i++){ // we need to add the neighbours not visited
if(!visited[rightAdjacent[node][i]]){
visited[rightAdjacent[node][i]] = true
arr = obj.path.slice(0);
arr.push(rightAdjacent[node][i]); queue.push({visited:JSON.parse(JSON.stringify(visited)),path:arr}) // deep copy of both
}
}
}
}
}
bfs()

xml get attributes javascript doesn't work on childNodes

I have the following code samples that try to display the d attribute of the svg map (found here).
// path method
var node = xmlDoc.getElementById('g67');
console.log(node);
for(var i = 0; i < node.getElementsByTagName('path').length; ++i) {
console.log(node.getElementsByTagName('path')[i].getAttribute('d'));
}
and
// childNodes method
var node = xmlDoc.getElementById('g67');
console.log(node);
for(var i = 0; i < node.childNodes.length; ++i) {
console.log(node.childNodes[i].getAttribute('d'));
}
The former works but the latter doesn't.
I understand I have gone wrong in my concept of nodes in XML DOM. Any help would be appreciated. Thanks You.
Complete code can be found here
childNodes gives you all child nodes. Text nodes, comments, processing instructions, everything.
You want to check the child type (name must be "path"):
var node = document.getElementById('g67'), child, i;
console.log(node);
for (i = 0; i < node.childNodes.length; i++) {
child = node.childNodes[i];
if (child.nodeName === 'path') {
console.log(child.getAttribute('d'));
}
}
Also note that childNodes gives you direct children only, while getElementsByTagName() gives you all descendants.

delete specific xml node Javascript

My xml file is like:
it contains different 'object' nodes and in different objects there are different parameters one is deleted parameter.
I want to delete the all 'object' nodes that contains the deleted parameter 1.
This is the code that deletes the node object which has a parameter node deleted =1:
x=xmlDoc.documentElement;
for(var count=0; count<5;count++){
var y=x.getElementsByTagName("deleted")[count]; //Find that nodes arent
if(y.textContent == "1") {
var z=y.parentNode; //delete the node from the parent.
x.removeChild(z);
Xml2String1= new XMLSerializer().serializeToString(x);
}
}
Your loop is incorrect:
for(var x1=0; x1<5;x1++){
var y=x.getElementsByTagName("deleted")[x1];
Your loop runs for 5 iterations without regard for the number of <deleted> elements are found. Each time through the loop you search again and get a new NodeList/HTMLCollection of the remaining <deleted> elements, but your loop counter is incremented regardless.
Try this instead:
var deletedNodesList = x.getElementsByTagName("deleted");
var nodesToDelete = [];
for (var index = 0; index < deletedNodes.length ; index += 1)
{
var node = deletedNodes[index];
if (node.textContent == "1")
{
nodesToDelete.push( node.parentNode ); //delete the node from the parent
}
}
nodesToDelete.forEach( function() { x.removeChild(this); } );
Note that, per the documentation on MDN, the NodeList is a live collection, so don't modify it while you are processing it.
PS.
I second raam86's recommendation to use sane (meaningful) variable names. Meaningful variable names make it easier to understand the code, which makes it easier to write correct code and to resolve problems in incorrect code.

Too much recursion

I'm using JS on firefox 4 and get the "too much recursion error" for the following code:
extractText: function(domObj) {
if (domObj == null) {
return "";
} else {
var acc = "";
if (domObj.nodeType == Node.TEXT_NODE) {
acc += domObj.nodeValue;
}
if (domObj.hasChildNodes()) {
var children = currentObj.childNodes;
for (var i = 0; i < children.length; i++) {
acc += sui.extractText(children[i]);
}
}
return acc;
}
}
};
Anyone?
I think that this line:
var children = currentObj.childNodes;
should be:
var children = domObj.childNodes;
It looks to me as if your reference to "currentObj" is starting over at the top instead of descending from the element under examination. It's hard to tell of course because you didn't include the relevant definition or initialization of "currentObj".
You can also try an iterative approach instead of recursion:
extractText: function(domObj) {
if (!(domObj instanceof Node)) return null;
var stack = [domObj], node, tf = [];
while (stack.length > 0) {
node = stack.pop();
switch (node.nodeType) {
case Node.TEXT_NODE:
tf.push(node.nodeValue);
break;
case Node.ELEMENT_NODE:
for (var i=node.childNodes.length-1; i>=0; i--)
stack.push(node.childNodes[i]);
break;
}
}
return tf.join("");
}
This algorithm implements a depth first search using a stack for the nodes that still must be visited. The first item on the stack is domObj if it’s a Node instance. Then for each node on the stack: if it is a Text node, its value is added to the text fragments array tf; if it’s an Element node, its child nodes are put on the stack in reverse order so that the first child is on top of the stack. These steps are repeated until the stack is empty. At the end, the text fragments in tf are put together using the array’s join method.

How do I make this loop all children recursively?

I have the following:
for (var i = 0; i < children.length; i++){
if(hasClass(children[i], "lbExclude")){
children[i].parentNode.removeChild(children[i]);
}
};
I would like it to loop through all children's children, etc (not just the top level). I found this line, which seems to do that:
for(var m = n.firstChild; m != null; m = m.nextSibling) {
But I'm unclear on how I refer to the current child if I make that switch? I would no longer have i to clarify the index position of the child. Any suggestions?
Thanks!
Update:
I'm now using the following, according to answer suggestions. Is this the correct / most efficient way of doing so?
function removeTest(child) {
if (hasClass(child, "lbExclude")) {
child.parentNode.removeChild(child);
}
}
function allDescendants(node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
allDescendants(child);
removeTest(child);
}
}
var children = temp.childNodes;
for (var i = 0; i < children.length; i++) {
allDescendants(children[i]);
};
function allDescendants (node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
allDescendants(child);
doSomethingToNode(child);
}
}
You loop over all the children, and for each element, you call the same function and have it loop over the children of that element.
Normally you'd have a function that could be called recursively on all nodes. It really depends on what you want to do to the children. If you simply want to gather all descendants, then element.getElementsByTagName may be a better option.
var all = node.getElementsByTagName('*');
for (var i = -1, l = all.length; ++i < l;) {
removeTest(all[i]);
}
There's no need for calling the 'allDescendants' method on all children, because the method itself already does that. So remove the last codeblock and I think that is a proper solution (á, not thé =])
function removeTest(child){
if(hasClass(child, "lbExclude")){
child.parentNode.removeChild(child);
}
}
function allDescendants (node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
allDescendants(child);
removeTest(child);
}
}
var children = allDescendants(temp);
You can use BFS to find all the elements.
function(element) {
// [].slice.call() - HTMLCollection to Array
var children = [].slice.call(element.children), found = 0;
while (children.length > found) {
children = children.concat([].slice.call(children[found].children));
found++;
}
return children;
};
This function returns all the children's children of the element.
The most clear-cut way to do it in modern browsers or with babel is this. Say you have an HTML node $node whose children you want to recurse over.
Array.prototype.forEach.call($node.querySelectorAll("*"), function(node) {
doSomethingWith(node);
});
The querySelectorAll('*') on any DOM node would give you all the child nodes of the element in a NodeList. NodeList is an array-like object, so you can use the Array.prototype.forEach.call to iterate over this list, processing each child one-by-one within the callback.
If you have jquery and you want to get all descendant elements you can use:
var all_children= $(parent_element).find('*');
Just be aware that all_children is an HTML collection and not an array. They behave similarly when you're just looping, but collection doesn't have a lot of the useful Array.prototype methods you might otherwise enjoy.
if items are being created in a loop you should leave a index via id="" data-name or some thing. You can then index them directly which will be faster for most functions such as (!-F). Works pretty well for 1024 bits x 100 items depending on what your doing.
if ( document.getElementById( cid ) ) {
return;
} else {
what you actually want
}
this will be faster in most cases once the items have already been loaded. only scrub the page on reload or secure domain transfers / logins / cors any else and your doing some thing twice.
If you use a js library it's as simple as this:
$('.lbExclude').remove();
Otherwise if you want to acquire all elements under a node you can collect them all natively:
var nodes = node.getElementsByTagName('*');
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (hasClass(n, 'lbExclude')) {
node.parentNode.removeChild(node);
}
}
To get all descendants as an array, use this:
function getAllDescendants(node) {
var all = [];
getDescendants(node);
function getDescendants(node) {
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
getDescendants(child);
all.push(child);
}
}
return all;
}
TreeNode node = tv.SelectedNode;
while (node.Parent != null)
{
node = node.Parent;
}
CallRecursive(node);
private void CallRecursive(TreeNode treeNode)
{
foreach (TreeNode tn in treeNode.Nodes)
{
//Write whatever code here this function recursively loops through all nodes
CallRecursive(tn);
}
}

Categories

Resources