Traversing Tree Bug - javascript

I've built a simple pseudoclassical Tree,
I have a method that applies a callBack to every node in the Tree and returns that node to a copy of its parent.
The problem comes at the end where the Tree is returned I don't get the last children in the original Tree?
code I pasted from snipets from chrome dev tools -
var Tree = function(value) {
this.value = value;
this.children = [];
};
Tree.prototype.map = function(callBack) {
let traverseTree = function(parent) {
let newParent = new Tree(callBack(parent.value));
if (parent.children) {
for (let i = 0; i < parent.children.length; i++) {
newParent.addChild(callBack(parent.children[i].value));
console.log(parent.children[i], 'each child', 'child in parent?', newParent)
traverseTree(parent.children[i]);
}
}
return newParent
};
return traverseTree(this)
}
Tree.prototype.addChild = function(value) {
let newChild = new Tree(value);
this.children.push(newChild);
return newChild;
};
var root1 = new Tree(1);
var branch2 = root1.addChild(2);
var branch3 = root1.addChild(3);
var leaf4 = branch2.addChild(4);
var leaf5 = branch2.addChild(5);
var leaf6 = branch3.addChild(6);
var leaf7 = branch3.addChild(7);
var newTree = root1.map(function(value) {
return value * 2;
});
console.log(newTree, "NEW TREEE")

The problem is that you don't do anything with the new node that is created with newParent.addChild(). So that created child will never be able to get children of its own.
You should actually not use that call at all, but leave it to the recursive call to create the node (which it does in its first statement), and then you should capture the return value from the recursive call, as that is the new child that has been created, and then append that child to the current node's children array.
One other comment: if (parent.children) is always going to lead to the execution of the if block, because every array, even when empty, is truthy in JavaScript. In fact, you don't need that if at all: the for loop will just not iterate if the array turns out to be empty.
So, taken that together, you get this:
var Tree = function(value) {
this.value = value;
this.children = [];
};
Tree.prototype.map = function(callBack) {
let traverseTree = function(parent) {
let newParent = new Tree(callBack(parent.value));
for (let i = 0; i < parent.children.length; i++) {
// Change is here:
newParent.children[i] = traverseTree(parent.children[i]);
}
return newParent;
};
return traverseTree(this);
}
Tree.prototype.addChild = function(value) {
let newChild = new Tree(value);
this.children.push(newChild);
return newChild;
};
var root1 = new Tree(1);
var branch2 = root1.addChild(2);
var branch3 = root1.addChild(3);
var leaf4 = branch2.addChild(4);
var leaf5 = branch2.addChild(5);
var leaf6 = branch3.addChild(6);
var leaf7 = branch3.addChild(7);
var newTree = root1.map(function(value) {
return value * 2;
});
console.log("new tree:");
console.log(JSON.stringify(newTree, null, 2));
I would like to take the opportunity to point you to some more modern syntax (class, spread syntax, arrow functions), and a constructor that can immediately accept the children nodes, making the addChild method unnecessary -- at least for this task:
class Tree {
constructor(value, ...children) {
this.value = value;
this.children = children;
}
map(callBack) {
const traverseTree = parent =>
new Tree(
callBack(parent.value),
...parent.children.map(traverseTree)
);
return traverseTree(this);
}
}
var root1 = new Tree(1,
new Tree(2,
new Tree(4), new Tree(5)
),
new Tree(3,
new Tree(6), new Tree(7)
)
);
var newTree = root1.map(value => value * 2);
console.log("new tree");
console.log(newTree);

Related

arguments.length run the function if 3 arguments are passed, otherwise throw an error object

TasksI need to modify the displaySortedTaskList function so that it runs if there are 3 arguments passed, and throws an error object with a message if there aren't 3 arguments passed. My attempt:
"use strict";
var sortTaskList = function(tasks) {
var isArray = Array.isArray(tasks);
if (isArray) {
tasks.sort();
}
return isArray;
};
var displaySortedTaskList = function(tasks, div, handler) {
if(arguments.length = Function.length){
var html = "";
var isArray = sortTaskList(tasks);
if (isArray) {
//create and load html string from sorted array
for (var i in tasks) {
html = html.concat("<p>");
html = html.concat("<a href='#' id='", i, "'>Delete</a>");
html = html.concat(tasks[i]);
html = html.concat("</p>");
}
div.innerHTML = html;
// get links, loop and add onclick event handler
var links = div.getElementsByTagName("a");
for (var i = 0; i < links.length; i++) {
links[i].onclick = handler;
}
}
} else {document.getElementById("message").innerHTML = "The displaySortedTaskList function of the tasklist library requires three arguments"}
};
var deleteTask = function(tasks, i) {
var isArray = sortTaskList(tasks);
if (isArray) { tasks.splice(i, 1); }
};
var capitalizeTask = function(task) {
var first = task.substring(0,1);
return first.toUpperCase() + task.substring(1);
};
You might use rest parameters and check whether the length of the array is 3:
var displaySortedTaskList = function(...args) {
if (args.length !== 3) {
document.getElementById("message").textContent = "The displaySortedTaskList function of the tasklist library requires three arguments";
return;
// or `throw new Error('not enough args')` ?
}
const [tasks, div, handler] = args;
// rest of your code
(note that you should assign to .textContent when inserting text - .innerHTML is appropriate when inserting HTML markup, which is not the case here)
Live snippet:
var displaySortedTaskList = function(...args) {
if (args.length !== 3) {
return console.log('error');
}
console.log('rest of the code');
}
displaySortedTaskList('foo', 'bar');
displaySortedTaskList('foo', 'bar', 'baz');
displaySortedTaskList('foo', 'bar', 'baz', 'buzz');

given an array representing a hierachy, output data into a tree form in JS

Given a data file which has an array representing a hierarchy. Create a tree data structure by writing a script in Javascript. Output the data in tree form:
Data file:
["transportation.cars.Mazda",
"transportation.cars.Honda",
"transportation.cars.Toyota",
"transportation.train.lightRail",
"transportation.train.rapidTransit",
"transportation.waterVehicle.ferry",
"transportation.waterVehicle.boats"
...]
Output in tree form:
root
transportation
cars
Mazda
Honda
Toyota
train
lightRail
rapidTransit
waterVehicle
ferry
boats
My attempt:
var root = new Node('root');
var arr = ["transportation.cars.Mazda",
"transportation.cars.Honda",
"transportation.cars.Toyota",
"transportation.train.lightRail",
"transportation.train.rapidTransit",
"transportation.waterVehicle.ferry",
"transportation.waterVehicle.boats"
]
for(var i of arr){
var res=i.split(".");
root.addChild(new Node(res[0]));
res[0].addChild(new Node(res[1]));
res[1].addChild(new Node(res[2]));
}
this.addChild = function(node) {
node.setParentNode(this);
this.children[this.children.length] = node;
}
console.log(root);
I am trying to create a tree structure using JavaScript, but it does not has the same function as in Java (i.e. it does not have class method unless using Typescript. )
You can use something similar to a trie tree. The way you add a node would have to be much more specific. But it's possible with something like this.
function Node(word)
{
this.value = word;
this.children = {};
}
function AddDotChain(chain)
{
let arr = chain.split('.');
let currentNode = this;
function recurse(currentIndex)
{
if(currentIndex === arr.length)
{
return;
}
let currentWord = arr[currentIndex];
if(currentNode.children[currentWord])
{
currentNode = currentNode[currentWord];
return recurse(currentIndex + 1);
}
let child = new Node(currentWord);
currentNode.children[currentWord] = child;
currentNode = child;
return recurse(currentIndex + 1);
}
}
Where you just slap the entire chain in there without splitting it. There's probably a flaw in my logic somewhere but the overall idea should work. This can also be done iteritavely if you wanna reduce the overhead of recursion. Forgive the messiness, Tried to type this as fast as possible.
Here's a sloppy sloppy implementation on repl.it.
You can do it, with a data structure as Tree, you only need loop over the array of string that contains the data and split them by dot and then add each item to the tree instance that will be created when you execute the function that take your array and output as a Tree data structure.
this code can help you
var arr = ["transportation.cars.Mazda",
"transportation.cars.Honda",
"transportation.cars.Toyota",
"transportation.train.lightRail",
"transportation.train.rapidTransit",
"transportation.waterVehicle.ferry",
"transportation.waterVehicle.boats"
];
function Node(data) {
this.data = data;
this.children = [];
}
function Tree(data) {
this.root = null;
}
Tree.prototype.contains = function(data) {
return this.find(data) ? true : false;
}
Tree.prototype.add = function(data, node) {
const newNode = new Node(data);
if (!this.root) {
this.root = newNode;
return;
}
const parent = node ? this.find(node) : null;
if (parent) {
if (!this.contains(data)) {
parent.children.push(newNode);
}
}
}
Tree.prototype.find = function(data) {
if (this.root) {
const queue = [this.root];
while(queue.length) {
const node = queue.shift();
if (node && node.data === data) {
return node;
}
for(var i = 0; i < node.children.length; i++) {
const child = node.children[i];
queue.push(child);
}
}
}
return null;
}
function createTreeOfTransportation(arr) {
const tree = new Tree();
for(var i = 0; i < arr.length; i++) {
const element = arr[i];
const nodes = element.split('.');
for (var j = 0; j < nodes.length; j++) {
const currentNode = nodes[j];
const parent = nodes[j-1];
console.log(j, parent);
tree.add(currentNode, parent);
}
}
return tree;
}
console.log(createTreeOfTransportation(arr));

Improve the performance of recursion for generating tree using JSON data

I need to construct a tree structure from data represented in JSON as object and parent relationship. I have implemented below code which is successfully doing the job but I am not sure whether it's giving the best performance (I mean doing the job in as less as possible iteration).
Please Note, the root of the tree is represented as parent is same as object. e.g. {"object":"A", "parent":"A"}
Suggestions about any other implementation with better performance would be helpful!!
var jsonInput =
[
{"object":"A", "parent":"A"},
{"object":"B", "parent":"A"},
{"object":"C", "parent":"A"},
{"object":"D", "parent":"B"},
{"object":"E", "parent":"B"},
{"object":"F", "parent":"D"},
{"object":"G", "parent":"D"},
{"object":"H", "parent":"E"},
{"object":"I", "parent":"E"},
{"object":"J", "parent":"C"},
{"object":"K", "parent":"C"},
{"object":"L", "parent":"J"},
{"object":"M", "parent":"J"},
{"object":"N", "parent":"K"},
{"object":"O", "parent":"K"},
{"object":"P", "parent":"N"},
{"object":"Q", "parent":"N"},
{"object":"R", "parent":"O"},
{"object":"S", "parent":"O"}
];
var root = getRoot();
root.childs = findChildrens(root);
console.log("The tree hierarchy is:")
console.log(root);
function getRoot() {
var root;
for (var counter = 0; counter < jsonInput.length; counter++){
var item = jsonInput[counter];
if(item.object === item.parent) {
root = item;
break;
}
}
var returnValue = JSON.parse(JSON.stringify(root));
root.visited = true;
return returnValue;
}
function findChildrens(parentObject) {
var childs = [];
for (var counter = 0; counter < jsonInput.length; counter++){
var item = jsonInput[counter];
if(item.parent === parentObject.object && !item.visited) {
var child = JSON.parse(JSON.stringify(item));
item.visited = true;
child.childs = findChildrens(child);
childs.push(child);
}
}
return childs;
}
A simpler solution with a linear runtime.
var data = [
{"object":"A", "parent":"A"},
{"object":"B", "parent":"A"},
{"object":"C", "parent":"A"},
{"object":"D", "parent":"B"},
{"object":"E", "parent":"B"},
{"object":"F", "parent":"D"},
{"object":"G", "parent":"D"},
{"object":"H", "parent":"E"},
{"object":"I", "parent":"E"},
{"object":"J", "parent":"C"},
{"object":"K", "parent":"C"},
{"object":"L", "parent":"J"},
{"object":"M", "parent":"J"},
{"object":"N", "parent":"K"},
{"object":"O", "parent":"K"},
{"object":"P", "parent":"N"},
{"object":"Q", "parent":"N"},
{"object":"R", "parent":"O"},
{"object":"S", "parent":"O"}
];
var rootNodes = data.filter(function(node) {
if (node.object in this)
throw new Error("duplicate object " + node.object);
this[node.object] = node;
node.children = [];
if (node.parent === node.object) return true;
var parent = this[node.parent];
if (!parent)
throw new Error("invalid parent " + node.parent);
parent.children.push(node);
}, Object.create(null));
console.log(rootNodes);
.as-console-wrapper {
top: 0;
max-height: 100%!important
}

copy an object doesn't effect on my code

I suppose to copy a object source, while copy changes, sourceshould not change.The source code goes as follow:
layoutTreemap: function(source) {
var copy = jQuery.extend(true,{},source);
var select_polygon = this.get_selected_polygon();
var vt = d3.layout.voronoitreemap()
var layoutNodes = vt(copy);
return layoutNodes;
}
d3.layout.voronoitreemap = function() {
var hierarchy = d3.layout.hierarchy().sort(null),
root_polygon = [[0,0],[500,0],[500,500],[0,500]],
iterations = 100,
somenewvariable = 0;
function voronoitreemap(d, depth) {
var nodes = hierarchy(d),
root = nodes[0];
root.polygon = root_polygon;
root.site = null;
if (depth != null){
max_depth = depth;
}
else{
max_depth = "Infinity";
}
computeDiagramRecursively(root, 0);
return nodes;
}
function computeDiagramRecursively(node, level) {
var children = node.children;
if(node.parent) node.parent = null;
if (children && children.length && level < max_depth) {
node.sites = VoronoiTreemap.init(node.polygon, node); // can't say dataset, how about node?
VoronoiTreemap.normalizeSites(node.sites);
VoronoiTreemap.sites = node.sites;
VoronoiTreemap.setClipPolygon(node.polygon);
VoronoiTreemap.useNegativeWeights = false;
VoronoiTreemap.cancelOnAreaErrorThreshold = true;
var polygons = VoronoiTreemap.doIterate(iterations);
// set children polygons and sites
for (var i = 0; i < children.length; i++) {
children[i].polygon = polygons[i];
children[i].site = VoronoiTreemap.sites[i];
computeDiagramRecursively(children[i], (level + 1));
}
}
}
....
return d3_layout_hierarchyRebind(voronoitreemap, hierarchy);
}
But after execute vt(copy), the source has been changed.
This gives issues
root.polygon = root_polygon;
children[i].polygon = polygons[i];
You are copying arrays you think. but actually you are copying the reference to the array. now you have two pointers to the same object(imagine two people using the same fork at dinner)
You need to change this into
root.polygon = root_polygon.slice();
children[i].polygon = polygons[i].slice();
this way the array gets copied instead of referenced. Now each dinner guest has its own fork.

Array being reset to undefined after adding items to it

I am trying to construct a hierarchy (tree structure) using JavaScript. For that, I wrote a Node class that represents a node in the tree. When I retrieve the data from the database, it's all being retrieved properly (i.e: the root node has the ParentId as null, it has 3 children that point to it as the parent, and the descendant nodes are set up properly as well...). But when I try to map them to my JavaScript model, the Children property of the root node is ending up being undefined. I do not know how that could be posible even though during runtime, when I output the contents of the Children property in the console I can see the children nodes being added to it. Here's my code:
var Node = function (obj) {
var self = this;
var isDefined = obj != undefined;
self.hasChildren = function () {
return self.Children.length > 0;
};
self.hasParent = function () {
var p = self.ParentId;
return !(p == null || p == undefined || p == 0);
};
self.addChildren = function (objArray) {
if (!$.isArray(self.Children)) {
self.Children = [];
}
for (var i = 0; i < objArray.length; i++) {
self.addChild(objArray[i]);
}
};
self.addChild = function (obj) {
if (obj instanceof Node) {
self.Children.push(obj);
} else {
var n = new Node(obj);
self.Children.push(n);
}
};
self.removeChild = function (n) {
var index = self.Children.indexOf(n);
if (index > -1) {
self.Children.splice(index, 1);
}
};
self.Id = isDefined ? obj.Id : null;
self.ParentId = isDefined ? obj.ParentId : null;
self.Name = isDefined ? obj.Name : '';
self.Children = isDefined ? self.addChildren(obj.Children) : [];
self.TypeId = isDefined ? obj.TypeId : null;
};
The way I thought about the addChildren method, is that I would pass the raw JSON object coming from the server into the constructor of the Node object and then in case it has any children (which essentially have the same properties as the parent), addChildren will be called which will in turn create a new Node for each element. Eventually, the tree will be built recursively.
So where did I go wrong? Why does the Children property end up being undefined?
self.Children = isDefined ? self.addChildren(obj.Children) : [];
You are setting self.Children equal to the return of self.addChildren(). That function has no return.
Here is a couple things I would recommend
function Node(obj) {
// clean constructor moving function definitions to prototype
var self = this;
// ensure that we at least have an object passed in
obj = obj || {};
// default values at the top
self.Id = null;
self.ParentId = null;
self.Name = '';
self.Children = [];
self.TypeId = null;
// fold in data with $.extend, no need to specify each key manually
// third object is to overwrite any added Children as those need to be handled seperately
$.extend(self, obj, { Children : [] });
// if we have children, add them using the addChildren method
if (typeof obj.Children !== undefined && $.isArray(obj.Children)) {
self.addChildren(obj.Children);
}
}
// using prototype to reduce memory footprint
Node.prototype.hasChildren = function () {
return this.Children.length > 0;
};
Node.prototype.hasParent = function () {
var p = this.ParentId;
return !(p == null || p == undefined || p == 0);
};
Node.prototype.addChildren = function (objArray) {
for (var i = 0; i < objArray.length; i++) {
this.addChild(objArray[i]);
}
};
Node.prototype.addChild = function (obj) {
if (obj instanceof Node) {
this.Children.push(obj);
} else {
var n = new Node(obj);
this.Children.push(n);
}
};
Node.prototype.removeChild = function (n) {
var index = this.Children.indexOf(n);
if (index > -1) {
this.Children.splice(index, 1);
}
};
Then I can use this like so:
test = new Node({ Id : "Something", Children : [{ Id : "Interior", Children : [] }] })
Using prototype you reduce the memory footprint and don't create a function reference to each interior function for each Node you create. Each Node one still will reference it's internal data via a this variable.

Categories

Resources