copy an object doesn't effect on my code - javascript

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.

Related

Traversing Tree Bug

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);

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));

Why is function not being called?

I have tried to code a tree that has a structure like that of a file tree. I am trying to run a piece of code (tree.prototype.traversalBF) in the 'tree.prototype.add' code.
issue is here:
Tree.prototype.traversalBF = function(node, pathPart) {
//determines number of children of the given node
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
var length = node.children.length;
var i = 0;
var found = false;
console.log(node);
//cycles through until there is a match
while( found != true && i < length){
if(node.children[i] == pathPart){
found = true;
//when there is a match it returns the node
return node.children[i];
} else if( i = length) {
var nodeFile = new Node(pathPart);
//adds the file name onto the the node
node.children.push(nodeFile);
//sets the node parent to the currentNode
nodeFile.parent = node;
}
i++;
}
}
Tree.prototype.add = function(path){
var pathSplit = path.split('/');
//gets the length of the path
var pathLength = pathSplit.length;
console.log(pathLength);
//this compares the path to the nodes/directories
let compare = (currentNode, n) => {
//console.log(n);
if(n == pathLength -1){
console.log(pathLength+ "secons");
console.log(currentNode.data)
//create a new node with file name as data
var nodeFile = new Node(pathSplit[n]);
//adds the file name onto the the node
currentNode.children.push(nodeFile);
//sets the node parent to the currentNode
nodeFile.parent = currentNode;
}else{
var newNode = () => this.traversalBF(currentNode, pathSplit[n]);
console.log(newNode);
compare(newNode, n+1);
};
};
compare(this._root, 0);
};
the else statment is run but the traversalBF is not run given the console.log('!!!!...!!!') doesn't log.

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
}

Filtering an array of Objects in javascript

I'm really new to JS, and I'm now stuck on a task, hope someone can guide me through it.
I have an Array of Objects, like this one:
var labels = [
// labels for pag 1
{pageID:1, labels: [
{labelID:0, content:[{lang:'eng', text:'Txt1 Eng'}, {lang:'de', text:'Txt1 De:'}]},
{labelID:1, content:[{lang:'eng', text:'Txt 2 Eng:'}, {lang:'de', text:'Txt2 De:'}]},
{labelID:2, content:[{lang:'eng', text:'Txt 3 Eng:'},{lang:'de', text:'Txt 3 De:'}]}
]},
// labels for pag 2
{pageID:2, labels: [
{labelID:0, content:[{lang:'eng', text:'Txt1 Eng'}, {lang:'de', text:'Txt1 De:'}]},
{labelID:1, content:[{lang:'eng', text:'Txt 2 Eng:'}, {lang:'de', text:'Txt2 De:'}]},
{labelID:2, content:[{lang:'eng', text:'Txt 3 Eng:'},{lang:'de', text:'Txt 3 De:'}]}
]}
]
What I am trying to do is write a function to return me an array of labels (Objects) for a specific page and a specific lang. By calling this function specifying pageID 1 and lang eng, I'm basically trying to build an array like this one:
var desideredArray = [
{labelID:0, text:'Txt1 Eng'},
{labelID:1, text:'Txt1 Eng'},
{labelID:2, text:'Txt2 Eng'}
]
Now, I'm trying to write the function to retrieve/build the new array:
this.getLabelsForPageAndLang = function (numPage, lang) {
// this part filters the main object and selects the object with pageID == numPage
var result = labels.filter(function( obj ) {
return obj.pageID == numPage;
});
var tempResult = result[0].labels;
var desiredResults = []; // here I want to store the new objects
for (var i=0; i<tempResult.length; i++) {
var simpleLabelObject = {};
simpleLabelObject.labelID = tempResult[i].labelID;
// simpleLabelObject.text = ?????
results[i] = simpleLabelObject;
}
console.log (results);
};
...but how can I access the right value (the one corresponding the lang selected) in the content property?
You can use the same technique as the one used to keep the matching page: the filter method.
this.getLabelsForPageAndLang = function (numPage, lang) {
// this part filters the main object and selects the object with pageID == numPage
var result = labels.filter(function( obj ) {
return obj.pageID == numPage;
});
var contentFilter = function(obj){ return obj.lang === lang};
var tempResult = result[0].labels;
var desiredResults = []; // here I want to store the new objects
for (var i=0; i<tempResult.length; i++) {
var simpleLabelObject = {};
simpleLabelObject.labelID = tempResult[i].labelID;
var matching = tempResult[i].content.filter(contentFilter);
simpleLabelObject.text = matching[0].text;
desiredResults[i] = simpleLabelObject;
}
console.log (desiredResults);
};
I didn't do bound checks because in your code you assumed there is always a matching element, but it would probably be wise to do it.
And if you want to avoid creating two closures each time the function is called, you can prototype an object for that:
var Filter = function(numPage, lang) {
this.numPage = numPage;
this.lang = lang;
};
Filter.prototype.filterPage = function(obj) {
return obj.pageID === this.numPage;
}
Filter.prototype.filterLang = function(obj) {
return obj.lang === this.lang;
}
Filter.prototype.filterLabels = function(labels) {
var result = labels.filter(this.filterPage, this);
var tempResult = result[0].labels;
var desiredResults = []; // here I want to store the new objects
for (var i=0; i<tempResult.length; i++) {
var simpleLabelObject = {};
simpleLabelObject.labelID = tempResult[i].labelID;
var matching = tempResult[i].content.filter(this.filterLang, this);
simpleLabelObject.text = matching[0].text;
desiredResults[i] = simpleLabelObject;
}
return desiredResults;
}
console.log(new Filter(1, "eng").filterLabels(labels));
Just filter again:
var getLabelsForPageAndLang = function (numPage, lang) {
// this part filters the main object and selects the object with pageID == numPage
var result = labels.filter(function (obj) {
return obj.pageID == numPage;
});
var tempResult = result[0].labels;
var desiredResults = []; // here I want to store the new objects
for (var i = 0; i < tempResult.length; i++) {
var simpleLabelObject = {};
simpleLabelObject.labelID = tempResult[i].labelID;
var lg = tempResult[i].content.filter(function (lg) {
return lg.lang == lang;
});
simpleLabelObject.text = lg[0].text;
desiredResults.push(simpleLabelObject);
}
console.log(desiredResults);
};
http://jsfiddle.net/9q5zF/
A rather 'safe' implementation for cases when pages have the same pageID and multiple contents with the same lang:
this.getLabelsForPageAndLang = function(numPage, lang) {
var result = [];
var pages = labels.filter(function( obj ) {
return obj.pageID === numPage;
});
for (var p = pages.length - 1; p >= 0; p--) {
var page = pages[p];
for(var i = page.labels.length - 1; i >= 0; i--) {
var labelId = page.labels[i].labelID;
for (var j = page.labels[i].content.length - 1; j >= 0; j--){
if (page.labels[i].content[j].lang === lang) {
result.push({labelID: labelId, test: page.labels[i].content[j].text});
}
}
}
}
console.log(result);
}
Fiddle: http://jsfiddle.net/6VQUm/

Categories

Resources