I'm working with P5.js and try to save some values in an array and than creating a copy of this array to manipulate.
Unfortunately It happens that when I manipulate the second array, also the original one changes, and I can't figure out why.
var particels = []
var particelsCopy = []
function calcInitialPositions(){
for (var i = 0; i < pixels.length; i+=4) {
if (pixels[i] == 0){
var x_ = i % width
var y_ = i / width / 2
var coords_ = {x : x_ , y : y_}
particels.push(coords_)
}
};
}
function setup() {
loadPixels()
calcInitialPositions();
particelsCopy = particels
}
function draw() {
for (var i = 0; i < particelsCopy.length; i++) {
particelsCopy[0].x = 99
};
console.log(particel[0].x)
}
Console prints 99
The = operator in Javascript assigns Objects, which includes arrays, by reference not by value. So the line:
particelsCopy = particels
is redefining particelsCopy to be an alias of particels.... after that point they are the same array. You need to copy the array by value like:
particelsCopy = particels.slice();
Note this is only a shallow copy, if the array contains objects or arrays they will be copied by reference, you will have to repeat this on child items (e.g. coords_ object, though for objects the pattern is copy = Object.assign({},original);).
To deep copy everything by value, you have to do this for every child level of object/arrays. Many libraries like jQuery have ready-built functions to help this.
You can use destructuring to copy objects in an array
particelsCopy = particels.map(obj => ({...obj}));
The line:
particelsCopy = particels
makes a copy of the array reference, not the elements in the array.
You need to allocate a new array object, then copy the elements. If the elements are objects, you will have to make a shallow (or deep) copy of them as well. This solution uses Object.assign() to make a shallow copy.
particelsCopy = [] // already done previously
for (var i=0; i<particels.length; i++){
particelsCopy[i] = Object.assign({}, particels[i]};
}
Related
I'm trying to populate a grid of coordinates into a multidimensional array using ES6 nested map methods using this code:
var gridSize = 4;
var regionArray = [];
for (var i = 0; i < gridSize; i++) regionArray.push(new Array(gridSize).fill({start:{x:0, y:0}, end:{x:0, y:0}}));
var regionWidth = canvas.width/gridSize;
var regionHeight = canvas.height/gridSize;
console.log(`regionWidth:${regionWidth} regionHeight:${regionHeight}`)
const hitRegions = regionArray.map((regionRow, i) => {
regionRow.forEach((region, j) => {
region.start.x = j*regionWidth;
region.end.x = j*regionWidth+regionWidth;
region.start.y = (i*regionHeight)+regionHeight;
region.end.y = (i*regionHeight)+regionHeight;
console.log(`>> region i:${i} j:${j} start.x:${region.start.x} end.x:${region.end.x} start.y:${region.start.y} end.y: ${region.end.y}`)
})
console.log('>> regionRow : ',regionRow)
return regionRow;
})
console.log(hitRegions);
The Issue I'm having is that the "regionRow" console.log is returning an array with all of the x values the same for each index.
Meanwhile the values in the ">> region" log above it is logging the correct values.
I'm trying to understand why this is happening and how to fix it..
In javascript everything is passed by reference, for this reason there are methods that do not directly alter the reference (Map, Filter, Reduce) in short, they return a copy of your array and it is modified on that copy.
This also happens with objects! Generally the spread operator is used to make a copy of an object and be able to modify the properties without obtaining side effects, like this:
const obj = {...oldObject}
The problem is when there are several levels in the object to mutate, Using the spread operator does not have a deep copy, therefore if we modify a property in a second level, we will be modifying the main object.
There are several options to fix this, and it will depend on how you are organizing your app:
You can use lodash (I think it's deprecated) with its cloneDeep method
You can use a library for immutability like ImmerJS
You can create your own method to clone
To quickly solve the problem you have, I did it using JSON.parse and JSON.stringify. This way you can have a copy and modify the deep objects.
The code would look like this:
const hitRegions = regionArray.map((regionRow, i) => {
const newRegionRow = regionRow.map((region, j) => {
var newObject = JSON.parse(JSON.stringify(region))
newObject.start.x = j * regionWidth;
newObject.end.x = j * regionWidth + regionWidth;
newObject.start.y = (i * regionHeight) + regionHeight;
newObject.end.y = (i * regionHeight) + regionHeight;
return newObject
})
return newRegionRow
})
console.log(hitRegions);
This solution is a bit dangerous if your objects have functions or a value that can't be directly converted to json, so be careful.
The method Array.prototype.fill() copies the value to each element.
When you put an object, it just copies the reference over. Meaning that if you update one element, all elements will be changed.
You can simply use a nested for loop to get what you want:
for (let i = 0; i < gridSize; i++) {
const arr = new Array(gridSize);
for (let i = 0; i < gridSize; i++) {
arr.push({ start: { x: 1, y: 1 }, end: { x: 2, y: 2 } });
}
regionArray.push(arr);
}
I am using this script to remove JSON objects from an array, which both appear within the array and another JSON object:
var stageChildren = stage.sprites;
for (var i = 0; i < stageChildren.length; i++) {
for (var x in mainMenu) {
if (mainMenu[x] === stageChildren[i]) {
console.log(x);
}
}
}
To make this more understandable, lets say I had two objects called: object1 & object2.
Inside object1, there may be the same JSON object which also appears within object2. If that's the case, the object is removed from object1.
While this script works, I think it might have a huge impact on performance. Why? Well, there's about 50 separate objects within stageChildren, and 10 inside mainMenu. The script loops through the first object inside stageChildren, checks if that object is also inside mainMenu (by performing a for loop again), and moves onto the next 49 objects.
Is there a more optimized way of doing this?
var index = 0;
var stageChildren = stage.sprites;
for (var x in mainMenu) {
if (stageChildren.includes(mainMenu[x])) {
const result = stageChildren.includes(mainMenu[x])
var index = stageChildren.indexOf(result);
stageChildren.splice(index, 1);
}
}
I wrote some simple javascript code, but it does not return the expected result.
var arr = new Array();
var firstobj = {
cta: 0,
ccc: 0,
crc: 0
}
for (var ta = 1; ta <= 10; ta++) {
arr[ta] = firstobj;
}
arr[6].cta = 1;
console.log(arr);
I only change cta value of 6th element, but it changes for all elements.
Expected:
[{cta:0, ccc:0, crc:0}, ..., {cta:1, ccc:0, crc:0}, {cta:0, ccc:0, crc:0}, ...]
// ^ 6th element
Actual:
[{cta:1, ccc:0, crc:0, ...] // All have 1
Objects in Javascript are assigned by pointer (well technically JS doesn't have pointers, but it works like a pointer does in other languages). So, you have filled your array so that every element contains a pointer-like reference to the exact same object. Thus, when you change that object, you see the change everywhere because there's only one object and you changed it.
If you want to initialize an array with separate objects, you have to create a new object or clone an existing object and assign that separate object into each element of your array.
For example, you could do that like this:
var myArray = [];
var obj;
// Also, arrays start from 0, not from 1.
for (var i = 0; i < 10; i++) {
// create a new object for each item in the array
obj = {cta:0, ccc:0, crc:0};
myArray[i] = obj;
}
myArray[6].cta = 1;
document.write(JSON.stringify(myArray));
you assigned the same object reference to all values in the array
make it (use Object.create)
for (var ta = 1; ta <= 10; ta++)
{
arr [ta] = Object.create( firstobj );
}
When you save the same firstobj to your array it saved by reference so when you changing one of the values it changes all of them, my suggestion is to create a new object for each element in your array.
var arr = new Array();
for (var ta = 1; ta <= 10; ta++) {
arr [ta] = { cta:0,
ccc:0,
crc:0
};
}
arr[6].cta=1;
console.log(arr);
Only the 6th element's cta value must be 1, but all elements cta
values change.
all the elements in your array(apart from element at zero index , which would be undefined in your case) are referring to the same object so if you change property in one element it is refelected across all elements
Each element in array is referring to same object.
So when you change object value it is reflected in all elements.
I want to make a loop that makes arrays automatically and assign the values to it.
The problem is how to generate the array itself automatically.
for(var attGetter=1; attGetter <= num; attGetter++){
var catesArray1 = new Array();
for(var atttGetterArray=1; atttGetterArray <= series; attGetterArray++){
idOfInput = "cate"+chartGetter+"_series"+attGetterArray;
catesArray1.push($("#"+idOfInput).val());
}
}
I want the loop to generate the array itself automatically like
catesArray1
catesArray2
catesArray3
and so on..
You need an object or an array to hold the multiple arrays you wish to create. Maybe something you are looking for is like the following?
var arrayHolder = new Array();
for(var attGetter=1; attGetter <= num; attGetter++){
var catesArray = new Array();
for(var attGetterArray=1; atttGetterArray <= series; attGetterArray++){
idOfInput = "cate"+chartGetter+"_series"+attGetterArray;
catesArray.push($("#"+idOfInput).val());
}
arrayHolder.push(catesArray);
}
If you want the arrays to be in global namespace, You can try
window['catesArray' + attGetter] = [];
...
window['catesArray' + attGetter].push(...)
Else you can create a hash object and use it to hold the reference
var obj = {};
.....
obj['catesArray' + attGetter] = [];
.....
obj['catesArray' + attGetter].push(...)
In that case you will have to create one new array that holds all the cacatesArrays from first for loop
var catesArrayContainer = new Array(); //<<<---------------------
for(var attGetter=1; attGetter <= num; attGetter++){
var catesArray = new Array();
for(var atttGetterArray=1; atttGetterArray <= series; attGetterArray++){
idOfInput = "cate"+chartGetter+"_series"+attGetterArray;
catesArray.push($("#"+idOfInput).val());
}
catesArrayContainer.push(catesArray); //<<<--------------------
}
EDIT :
This happens because the scope of variable catesArray1 was limited. When the loop enters next iteration the catesArray1 gets reinitialized, thus losing all the previously stored values...
Now in the code I have posted, we are storing every instance of catesArray1 in another array, and your values persist out side of the for loop
You can do something like this for 4 arrays of 5 elements each
yourarray=[];
for (i = 0; i <4; i++) {
temparray=[];
for (j = 0; j < 5; j++) {
temparray.push($('#'+whateverID+'_'+i+'_'+j)) //your values here
}
yourarray.push(temparray);
}
Check it on this JSFiddle. open chrome console to see array
If you want to create array within loop from index
You can use eval to evaluate javascript from strings but i wont use that unless there is no other way. you can see both above and eval method in this Fiddle. Open Chrome console to see array values
Just a comparison of using eval and 2D array
Open console in chrome while you run this jsFiddle and you will see the difference in eval and 2darray in context of this question.
You should assign them to an object. In this case, make an object variable before the first for-loop to hold all arrays:
var allArrays = {};
for(var attGetter=1; attGetter <= num; attGetter++){
var currentArray = allArrays['catesArray' + attGetter] = new Array();
for(var atttGetterArray=1; atttGetterArray <= series; attGetterArray++){
idOfInput = "cate"+chartGetter+"_series"+attGetterArray;
currentArray.push($("#"+idOfInput).val());
}
}
Instead of attempting to create & allocate dynamically named variables, I would think of this more of an array of array's if you will. In other words, create an array that holds all of the arrays you want:
var collections = []; // Literal notation for creating an array in JS
From here, it's a matter of making each value you create within this array its own array:
var n = 10; // Total amount of arrays you want
for (var i = 0; i < n; i++) {
var values = [];
// Have another loop that fills in the values array as expected
collections.push(values); // Each element at collections[i] is its own array.
}
If you truly need named elements, you could potentially do something very similar with just an object {} instead, and refer to each element by a name you create.
var collections = {}; // Literal notation for an object in JS
var n = 10; // Total amount of arrays you want
for (var i = 0; i < n; i++) {
var values = []; // Literal notation for an array in JS
// Fill in the values array as desired
var name = 'arr' + i; // How you'll refer to it in the object
collections[name] = values;
}
I suggest the former though, since it does not sound like you need to have explicit names on your arrays, but just want multiple layers of arrays.
Okay so I am trying to make an AI for a game called notakto. That much isn't relevant however, in order to make the search algorithm that I do I need to duplicate array. So I have a global array called board which looks like this [[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]] where all of the 0s are different numbers.
To copy this I have the following line. var newboard=board.slice(). Problem is when I run a line of code like newboard[0][0]=1 it changes also acts as if I have run the following board[0][0]=1.
You copy your array, but the inner arrays are not copied. This is a litte bit hacky but it works:
var newboard = JSON.parse(JSON.stringify(board));
In this context it will work, but if your object has any functions these are lost through stringify. Moreover it could make some trouble with Data objects which are stored inside the object.
javascript is always reference based. if you want to make a duplicate copy, please do deep object copy instead of sallow copy.
In angular, angular.copy() will perform deep copy.
var newboard=angular.copy(board.slice());
It's because nested arrays are shallow-copied by reference. You can use a recursive function like this to deep-copy a multidimensional array such as the one you have above, making sure that each nested array is copied by value:
function copyMultidimensionalArray(array) {
var r = array.slice(0);
for (var i = 0, l = r.length; i < l; ++i) {
if (Array.isArray(r[i])) {
r[i] = copyMultidimensionalArray(r[i]);
}
}
return r;
}
/* example */
var board = [[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]],
anotherBoard = copyMultidimensionalArray(board);
anotherBoard[0][0] = 99;
document.write([
"board[0][0] ===",
board[0][0],
"&& anotherBoard[0][0] ===",
anotherBoard[0][0]
].join(" "));
You can also use board.map(function(cv){ return cv.slice();}) to copy the board:
var board = [[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 board2 = board.map(function(cv){ return cv.slice();});
board2[0][0] = 1;
console.log(board);
console.log(board2);