.slice() method in Javascript not copy by value - javascript

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

Related

Remove object from array if it also appears in JSON object

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

Adding two same rows in array than modifying only one of them

First, I am adding two same rows in array and later I need to modify only the last one, adding new property to it. The way I do that:
for(var index in arrayOne) {
var arrayOneItem = arrayOne[index];
var new_row = {
address: arrayOne[index].address,
date: arrayOne[index].date,
category: arrayOne[index].category,
};
rows.push(new_row);
if(arrayOne[index].refund_status == 'refunded') {
rows.push(new_row);
rows[rows.length - 1].refund_status = 'refunded';
}
}
But the problem is that the code inside if statement does not only modify last row, but also the one before it, so the refund_status = 'refunded' is added both to the last and one before last row. Why is this happening and is there a way to modify the last row only?
When you are using the same object twice it's best to create a copy (shallow in this case) using Object.assign(). This will avoid referencing the same object from multiple variables or array indexes in your case.
eg.
rows.push(new_row);
becomes
rows.push(Object.assign({}, new_row));
This is because the object you push into the array is passed by reference and not by value, thus when you change the original object you will change both references to it in the array, see example below:
let someArray = [];
let someObj = {foo: "bar"};
someArray.push(someObj);
someArray.push(someObj);
someArray[0].foo = "baz";
console.log(someArray[1]);
To avoid this, you would need to clone the values of the object to create a new one. This question has some ways to do so, using JSON.parse and JSON.stringify is the shortest way to deep-copy an object without an external library, see example below:
let someArray = [];
let someObj = {foo: "bar"};
someArray.push(someObj);
let newObj = JSON.parse(JSON.stringify(someObj));
someArray.push(newObj);
someArray[0].foo = "baz";
console.log(someArray[1]);
Because you are changing property of an object, and object in javaScript is accessed through a link not a separate instance. In another words, you have the same object in memory and you change its property. It means, new_row is object you create and push it several times, and it's the same.
You can avoid it by copying it when pushing second times:
if(arrayOne[index].refund_status == 'refunded') {
rows.push({ ...new_row });
rows[rows.length - 1].refund_status = 'refunded';
}
where { ...new_row } basically creates new copy.
When you do rows[rows.length - 1].refund_status = 'refunded'; only second last will change.
Another solution i'd suggest is a bit more accurate:
const rows = []; // empty
const arrayOne = []; // SOME DATA HERE as I understand
const refundedStatus = ;
arrayOne.forEach(element=> {
rows.push(element);
if (value.refund_status === 'refunded') {
rows[rows.length].refund_status = 'refunded';
rows.push({ ...element});
}
});

JavaScript - Filter array with mutation

I want to filter a array by keeping the same array without creating a new one.
with Array.filter() :
getFiltersConfig() {
return this.config.filter((topLevelConfig) => topLevelConfig.name !== 'origin')
}
what is the best way to get the same result by filtering by value without returning a new array ?
For completeness, I thought it might make sense to show a mutated array variant.
Below is a snippet with a simple function mutationFilter, this will filter the array directly, notice in this function the loop goes in reverse, this is a technique for deleting items with a mutated array.
Also a couple of tests to show how Array.filter creates a new array, and mutationFilter does not.
Although in most cases creating a new array with Array.filter is normally what you want. One advantage of using a mutated array, is that you can pass the array by reference, without you would need to wrap the array inside another object. Another advantage of course is memory, if your array was huge, inline filtering would take less memory.
let arr = ['a','b','a'];
let ref = arr; //keep reference of original arr
function mutationFilter(arr, cb) {
for (let l = arr.length - 1; l >= 0; l -= 1) {
if (!cb(arr[l])) arr.splice(l, 1);
}
}
const cond = x => x !== 'a';
const filtered = arr.filter(cond);
mutationFilter(arr, cond);
console.log(`ref === array -> ${ref === arr}`);
console.log(arr);
console.log(`ref === filtered -> ${ref === filtered}`);
console.log(filtered);
I want to filter a array by keeping the same array without creating a new one.
what is the best way to get the same result by filtering by value without returning a new array ?
I have an answer for the second criterion, but violates the first. I suspect that you may want to "not create a new one" specifically because you only want to preserve the reference to the array, not because you don't want to create a new array, necessarily (e.g. for memory concerns).
What you could do is create a temp array of what you want
var temp = this.config.filter((topLevelConfig) => topLevelConfig.name !== 'origin')
Then set the length of the original array to 0 and push.apply() the values "in-place"
this.config.length = 0; //clears the array
this.config.push.apply(this.config, temp); //adds what you want to the array of the same reference
You could define you custom method like so:
if(!Array.prototype.filterThis){
Array.prototype.filterThis = function (callBack){
if(typeof callBack !== 'function')
throw new TypeError('Argument must of type <function>');
let t = [...this];
this.length = 0;
for(let e of t) if(callBack(e)) this.push(e);
return this;
}
}
let a = [1,2,3,4,5,5,1,5];
a.filterThis(x=>x!=5);
console.log(a);
Warning: Be very cautious in altering built in prototypes. I would even say unless your making a polyfill don't touch. The errors it can cause can be very subtle and very hard to debug.
Not sure why would you want to do mutation but if you really want to do it, maybe assign it back to itself?
let arr = ['a','b','a'];
arr = arr.filter(x => x !== 'a');
console.log(arr)

javascript array seems to change my itself

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]};
}

Copying of an array of objects to another Array without object reference in javascript(Deep copy)

I have a scenario where i need to copy the array of Objects(Main array) to another Temp array which should not have object reference basically if i make any modification to Main array it should not reflect in the Temp array so that i will preserve the copy independently.
I have used one of the code snippet from stack overflow this one does partially like if i delete all objects from the Main array the temp array still hold the value but when i do some modifications in main array and click cancel button iam removing all objects from the main array using array.Removeall(); but the modification still exist in Temp array so which means that object having a reference.
clone: function (existingArray) {
var newObj = (existingArray instanceof Array) ? [] : {};
console.debug('newObj value is ' + newObj);
for (i in existingArray) {
console.debug('i value is' + i);
if (i == 'clone') continue;
console.debug('existingArray[i] value ' + existingArray[i]);
if (existingArray[i] && typeof existingArray[i] == "object") {
newObj[i] = this.clone(existingArray[i]);
} else {
console.debug('in else part ' + existingArray[i]);
newObj[i] = existingArray[i];
}
}
return newObj;
}
my object structure is like
iam using knockout framework.
newObjectCreation = function (localIp, RemoteIp, areaId) {
this.localIP = ko.observable(localIp);
this.remoteIP = ko.observable(RemoteIp);
this.areaId = ko.observable(areaId);
};
template.ProtocolArray.push(new newObjectCreation('', '', '')); // to create default row
Let me understand: you don't want just have a new array, but you want to create a new instance for all objects are present in the array itself? So if you modify one of the objects in the temp array, that changes is not propagated to the main array?
If it's the case, it depends by the values you're keeping in the main array. If these objects are simple objects, and they can be serialized in JSON, then the quickest way is:
var tempArray = JSON.parse(JSON.stringify(mainArray));
If you have more complex objects (like instances created by some your own constructors, html nodes, etc) then you need an approach ad hoc.
Edit:
If you don't have any methods on your newObjectCreation, you could use JSON, however the constructor won't be the same. Otherwise you have to do the copy manually:
var tempArray = [];
for (var i = 0, item; item = mainArray[i++];) {
tempArray[i] = new newObjectCreation(item.localIP, item.remoteIP, item.areaId);
}
For some other people with the same question. You could also do it this way.
Using the new es6 features you could create a copy of an array (without reference) and a copy of every object without one level of references.
const copy = array.map(object => ({ ...object }))
It's much more functional and idiomatic IMHO
Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it's the same with Object.assign() and spread syntax).
More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
So basically if your objects doesn't have objects as properties. This syntax is everything you need. Unfortunately there is not "out of the box" deep clone feature on the spec but you can always use a library if that's what you need
Browser Compatibility Warning: I think it is part of the specification of Ecma now, but some browsers doesn't have full support of spread syntax jet. But using one of the popular transpilers out there you will be fine
Lodash can be used for deep copying objects _.cloneDeep(value)
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// → false
To copy the values of an array without copying the reference of the array, you can simply do:
const tempArray = [...mainArray];
This is the recommended solution for AirBnb's JS Style Guide: https://github.com/airbnb/javascript#arrays
However, this will not create new referenes for the objects inside the array. To create a new reference for the array and the objects inside, you can do:
JSON.parse(JSON.stringify(mainArray));
So you want a deep copy without object reference? Sure, use .slice().
Example:
var mainArr = [],
tmpArr = []
tmpArr = mainArr.slice(0) // Shallow copy, no reference used.
PS: I don't think double-JSON parsing is performance wise.
You can use Angular's copy: angular.copy();
On nested array you can do:
const origin = [{ cat: 'Bengal', dog: 'Birman' }, { cat: 'Abyssinian', dog: 'Bombay' }];
const clone = [];
origin.forEach(v=> clone.push(Object.assign({}, v)));
Use angular.copy. But not for the whole array (because it would pass array items by reference), but iterate it and use angular.copy on its members.
var newArray = [];
for (var i = 0, item; item = mainArray[i];) {
newArray[i] = angular.copy(item);
i++;
}
Use this function
https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
let deepClonedArrayOfObjects = structuredClone(originalArrayOfObjects);

Categories

Resources