Javascript recursion for tree creation - javascript

i am trying to create funcion for creating decision tree with state of game in every node in game (doesnt matter what game). I wrote recursive function (DFS) like this:
function makeTree(anchor,count,player){
var subTree=null;
var nodes=[];
if(player)var newPlayer=false;
else var newPlayer=true;
for (var i = 0; i <= 9; i++) {
for (var j = 0; j <= 9; j++) {
if(anchor["state"][i][j]==0){
var newState=anchor["state"];
if(player)newState[i][j]=1;
else newState[i][j]=2;
var node={name:i+"_"+j, contents:[],state:newState, value:null, player:newPlayer};
if(count>0){
var newCount=count-1;
subTree=makeTree(node,newCount,newPlayer);
node["contents"]=subTree;
}
nodes.push(node);
}else{
continue;
}
}
}
return nodes;
}
And with call:
var tree={};
var hrac=true;
var plocha=[[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0]];
var state=plocha;
tree={name: "root",
contents:[],
state:state,
value:null,
player: hrac};
tree["contents"]=makeTree(tree,3,hrac);
But the function change variables in different scope, so the output tree will be like this:
"root" - node - node - node
- node
- node
- node
- node
I cant figure out what is going on with variable newState in that function, because after finish the recursion the original variable plocha has the value of the latest node["state"]. Any suggestions what to do?
EDIT: Thanks to Bergi i realize that i need to do deep copy of array insted of make reference to it, so i make funcion for copy of array and now this works. Thank you Bergi!

Your state property is an array, which is mutable. On every assignment, you change the one multidimensional array that is the state of all nodes. You'll want to make every newState a new array, instead of passing your plocha reference recursively through all functions:
…
var newState = anchor["state"].slice(); // create copy of the outer array
newState[i] = newState[i].slice(); // copy of the row to be modified
newState[i][j] = player ? 1 : 2;
…

Related

Problems with immutable array

Recently i started working with D3.js to plot a sunburn graph. The data is provided in JSON. For some design stuff i wanted to swap some items (called childrens in D3 doc).
I know in JS arrays are objects...so something like this:
var buffer = myarray[2];
is just a reference. Therefore a buffer for swapping has no effect (?).
Thus i invented a second array (childrens_final) in my code which adopt the items while the swapping process. Its just iterating through every item, a second iteration is looking for an item with the same name to set items with same name in a row. Therefore the swap.
var childrens = response_data.data.data['children'];
var childrens_final = []
for (var child = 0; child < childrens.length; child++) {
var category = childrens[child];
var found = false;
var i = child+1
while (!(found) && (i < childrens.length)) {
if (childrens[i]['name'] == category['name']) {
var childrens = swapArrayElements(childrens, child+1, i);
var one = childrens[child];
var two = childrens[child+1]
found = true;
}
i++;
}
if (found) {
childrens_final.push(one);
childrens_final.push(two);
child++;
}
else {
childrens_final.push(childrens[child])
}
}
response.data.data['children'] = childrens_final;
return response.data.data;
The function swapArrayElements() is just using splice:
function swapArrayElements(list, x, y) {
if (list.length ==1) return list;
list.splice(x, 1, list.splice(y,1, list[x])[0]);
return list;
}
The problem is that there is still no effect from the swap in the graph. But when logging the childrens_final. There is something like that in the console:
Array [ Object, Object, Object, Object, Object, Object, Object, Object, Object ]
The objects are in the right order! But in the array there is still the old order.
Thats actually so basic, but i dont see a solution.
Btw...the code is working under AngularJS.
Found the problem. It's a D3.js problem. D3 is sorting the data itself. You have to set explicitly:
d3.layout.partition.sort(null)
Otherwise every pre sorting process has no effect.

Javascript delete does not removed the desired property

I am using nodejs to create a game. It happens that this time I am not able to use Delete. It is not working and I have no idea why.
I execute this line:
delete Bullet.list[i]
And Bullet.list is an object with properties and after executing this line, the desired property is not removed.
Here is my code (you can see the above line of code in context here below):
var Map = require("./Map.js")
var Player = require("./Player.js");
var Bullet = require("./Bullet.js");
var Fly = require("./Fly.js");
var Settings = require("./Settings.js");
var Simulation = function(SOCKET_LIST){
//Update Bullets
for(var i in Bullet.list){
var bullet = Bullet.list[i];
bullet.update();
var shooter = Player.list[bullet.parent];
//Bullets collide with flies
for(var i in Fly.list){
var fly = Fly.list[i];
if(!bullet.toRemove && bullet.getDistance(fly) < 15){
if(shooter){
shooter.updateCoins(fly.killCoins);
shooter.updateXp(fly.killXp);
}
bullet.toRemove = true;
fly.toRemove = true;
}
}
//Collide with player
for(var i in Player.list){
var player = Player.list[i];
if(player.death) continue;
if(!bullet.toRemove && bullet.getDistance(player) < 32 && shooter !== player.id && shooter.team != player.team && !player.immune){
player.hp -= 1;
player.hpChanged = true;
if(player.hp <= 0){
if(shooter)
player.kill(shooter);
else
player.kill();
}
bullet.toRemove = true;
}
}
//Collide with map
if(typeof shooter == "undefined" || shooter.shootsCollideMap && Map.isColliding(bullet))
bullet.toRemove = true;
//Remove bullets
if(bullet.toRemove){
delete Bullet.list[i]; /*HERE IS THE PROBLEM. THE PROPERTY IS NOT BEING DELETED*/
Bullet.removePack.push[bullet.id];
}
}
I am not able to delete the property "i" from the object Bullet.list.
Your three for loops are all sharing the same variable i and thus you aren't deleting the i index you want to. This is because var i is function scoped, not scoped to the individual for loop.
When you do this:
delete Bullet.list[i];
The value of i is whatever i is after your previous for loop and probably not the item you actually want to delete. Perhaps you mean to break out of the for loop previously so that i will be a specific item you want to remove?
Or you need to separately save to another variable an index of an item that you want to remove. Or, just remove an item inside the for loop when the value of i is current.
Remember that var is function scoped. So both your var i declarations in your two nested for loops are actually referencing the exact same variable, not declaring a new one. I would recommend changing the name of the loop index in one of the two loops to be a separate variable.
If you are running in an environment where let is fully supported, you could use let i instead of var i and then the value of i would be uniquely scoped to only the for loop in which it was declared.
When you delete an array element, the array length is not affected. This holds even if you delete the last element of the array.
You could try Bullet.list.splice(i, 1) as an alternative to delete.
There is a lot going on in this code,, hope this helps!

Append Child will not work on concatenated array items

I have queried the DOM getting all the DOM elements with a particular tag (item), and stored them in an array. I then cloned that array and concatenated 2 copies of the clone to the array essentially tripling the amount of elements from 20 to 60. So after I am done I have a new array with 60 elements. When I try to loop through those 60 elements and surround every 6 of them with a parent div, appendChild method will not append the copies which were concatenated to the array. It will only work on the original 20 which I assume are from the original clone.
function buildProductsList_() {
var concatNumber = 1;
cloneItemArray = baseItemElementsByAttribute.slice();
if (baseItemElementsCount % baseItemElementsPerSlide == 0) {
return false;
}
while (baseItemElementsCount * concatNumber %
baseItemElementsPerSlide != 0) {
concatNumber++;
}
for (i = 0; i < concatNumber; i++) {
newItemList = newItemList.concat(cloneItemArray);
}
newItemListCount = newItemList.length;
offsetFrameCount = newItemListCount % baseItemElementsPerSlide;
}
function constructCarouselSlides_() {
for (i = 0; i < newItemListCount; i += baseItemElementsPerSlide) {
var offset = baseItemElementsPerSlide;
if (offsetFrameCount + i == newItemListCount) {
offset = offsetFrameCount;
}
var section = newItemList.slice(i, i + offset);
var itemsClones = itemsInstance.cloneNode();
for (j = 0; j < section.length; j++) {
itemsClones.appendChild(section[j]);
}
carouselContainer.appendChild(itemsClones);
}
}
See Screenshot: The first 6 items elements do not have any of the item elements appended to them. It seems to only have worked on the original 20 item elements. Any help would be appreciated.
Screenshot of DOM
https://www.dropbox.com/s/55n0y8j7ezet1u7/Screen%20Shot%202015-09-23%20at%203.00.50%20PM.png?dl=0
Making a copy of an array of DOM elements with .slice() does NOT make new DOM elements. It just makes a second array that contains references to the same set of DOM elements. So, when you try to append those same DOM element references from the cloned array, it just moves them from where they were originally.
If you want a new array of newly created DOM elements, you will have to clone each element in the array to actually create a new set of DOM elements. Here's a function that will clone an array of DOM nodes:
function cloneDOMArray(arr) {
return arr.map(function(item) {
return item.cloneNode();
});
}
It returns an array of cloned nodes.
More Explanation:
At the core of this is in Javascript, a primitive such as a number or boolean is assigned by copying the value.
var a = 2;
var b = a;
a = 3;
console.log(a); // 3 (shows the newly assigned value)
console.log(b); // 2 (it has a copy of the original value of a)
But, objects in Javascript (which includes DOM objects) are assigned by pointer:
var x = document.createElement("div");
x.innerHTML = "Hello";
var y = x;
x.innerHTML = "Goodbye";
console.log(y.innerHTML); // "Goodbye"
console.log(x.innerHTML); // "Goodbye"
So, when you assign one object to two different variables, each variable points at the exact same object. If you modify that object, you will see that modification through both variables (because they both point at the exact same object).
So, if you have an array of DOM elements references and you then make a copy of that array with .slice(), you will just have two arrays with the exact same set of DOM element references in it.
var x = document.getElementById("one");
var y = document.getElementById("two");
var items = [x,y];
var copyItems = items.slice(0);
console.log(items[0] === copyItems[0]); // true, same element reference
So, when you assign an object to a second variable and you want the second variable to contain a copy of that object, you have to explicitly make a copy. How you best make a copy depends upon the object (you would do it differently for an array of DOM element references vs. an array of something else).

Nested 'for' loop - array undefined

I am working on a JS where I want to create a simple game that starts by chosing number of players, name of each player and whether a player is a dealer or not. There can be only one dealer for each game:
function player(playerName, playerDealer) {
this.playerName = playerName;
this.playerDealer = playerDealer;
}
var playerNumber = prompt('Nr of players?');
var playersArray = [];
for (i = 0; i < playerNumber; i++) {
var j = i + 1;
var dealerAssigned = false; // control variable to check whether dealer has been assigned
var inputName = prompt('Name of player nr ' + j);
var inputDealer = prompt('Is player ' + inputName + ' also a dealer? (yes/no)');
playersArray[i] = new player(inputName, inputDealer);
for (k=0;k<playerNumber;k++){ // I want to go through the players array to check if dealer has been assigned
if (playersArray[k].playerDealer == 'yes') {
dealerAssigned=true;
break;
};
};
if(dealerAssigned){ //if dealer has been assigned, don't add the current player to the array and continue with the next iteration
alert("already assigned");
continue;
};
};
I need to include a simple test into the loop that would check if the dealer has been appointed. If so, I want the script only to alert 'already assigned' and skip to the next player. But I am constantly getting the following error
TypeError: playersArray[k] is undefined
Can anybody explain why is it undefined?/What am I doing wrong?
The bug you're specifically asking about appears to me to be that you're iterating over undefined array values, as the error you're getting suggests.
You're getting the number of players you want in line
var playerNumber = prompt('Nr of players?');
Then, you proceed to have two iterations (one nested in the other), in which the inner loop is trying to access values that haven't yet been assigned since the outer loop hasn't gotten there yet:
for (i = 0; i < playerNumber; i++) {
playersArray[i] = new player(inputName, inputDealer);
for (k=0; k < playerNumber; k++) {
if (playersArray[k].playerDealer == 'yes') {
...
}
}
}
It appears to me that the logical error here is the nested loop. I recommend just initializing all players in one loop, then verify that all players have an assigned dealer afterward.
I should add that I'm being intentionally myopic here and focusing very narrowly on the question asked and overlooking other issues I see.
Your for loop inside a for loop is iterating over an array that hasn't been filled yet.
First iteration playersArray[j] = new Player(...) makes the array [Player] or an array of one element! Yet the second loop is looking for an array of many elements. once you look for playersArray[1] but there is only playerArray[0] you get undefined and so undefined.playerDealer causes a TypeError.
`This is your structure stipped-down:
for (i = 0; i < playerNumber; i++) {
playersArray[i] = new player(inputName, inputDealer);
for (k=0;k<playerNumber;k++)...{
//anything with index k > i is undefined, since your outer loop
//hasn't initialized it yet.
}
}
It seems that your i-loop is trying to insert elements for the size of the array to be, but your k-loop is trying to also access the entire array instead of just the initialized portions. Limit this to for (k=0; k<i+1 ;k++) so you only check the previously initialized values of you playersArray

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.

Categories

Resources