Deep copy Javascript array with properties - javascript

Using .slice(), I can deep copy a Javascript Array of primitive types, for example:
var arr = [1,2,3,4];
var newArr = arr.slice();
newArr.push(5);
console.log(arr); // [1,2,3,4]
console.log(newArr); // [1,2,3,4,5]
However, If I add a property to arr like so:
arr.prop1 = 5;
and do the same:
var arr = [1,2,3,4];
arr.prop1 = 8;
arr.prop2 = 9;
var newArr = arr.slice();
newArr.push(5);
console.log(arr); // [1, 2, 3, 4, prop1: 9, prop2: 8]
console.log(newArr); // [1, 2, 3, 4, 5]
The property values do not carry over to newArr
I have considered not using .slice() and looping over the property values of arr instead, assigning them to newArr, using :
for (var key in arr) {
if (arr.hasOwnProperty(key)) {
newArr[key] = arr[key];
}
}
console.log(arr); // [1, 2, 3, 4, prop1: 9, prop2: 8]
console.log(newArr); // [1, 2, 3, 4, 5, prop1: 9, prop2: 8]
Is this going to be the quickest way to deep copy these arrays with properties? Is there in easier or cleaner way I can do this using .slice() or another array method? I am doing this operation tens of thousands of times and want it to be as clean and efficient as possible

You are trying to mix an array and object(to make an array behave like an object).
JavaScript array has numeric indexed items.
JavaScript object has string indexed items.
Arrays have a length property that tells how many items are in the array and is automatically updated when you add or remove items to the array.But ...
var arr = [1,2,3,4], newArr = arr.slice();
arr.prop1 = 7;
console.log(arr.length);
The output is 4, though you would expect it to be 5.But arr.prop1 = 7 does not actually add to the array.Setting a string parameter adds to the underlying object.
The length property is only modified when you add an item to the array, not the underlying object.The expression newArr = arr.slice() assigns a new array to newArr, so it remains an array and behaves like a pure array.
The property values do not carry over to newArr
If you still want to proceed using mixed numeric/string indexed sequences, cloning them, try using Object.assign() function. This should be the easiest way for your case:
The Object.assign() method is used to copy the values of all
enumerable own properties from one or more source objects to a target
object.
console.log(Object.assign(newArr, arr));
The output:
[1, 2, 3, 4, prop1: 7]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

How about using Object.create(proto)
var arr = [1,2,3,4];
arr.prop1 = 8;
arr.prop2 = 9;
var newArr = Object.create(arr);
newArr.prop1 = 12;
console.log(arr.prop1) // 8
console.log(newArr.prop1) // 12
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

Related

Assign the same array object to multiple variables? [duplicate]

This question already has answers here:
javascript for loop changes original list variable
(3 answers)
Closed 2 years ago.
From what I understand, when an array object is assigned to a new variable, that array object can be "referenced" between both variables, but the values themselve are mutable by either assigned variable.
At least that appears to be the case.
let variable1 = [6, 3, 2, 6, 7, 2, 9, 1, 5];
let variable2 = variable1;
for (i = 0; i < 10; i++) {
variable2.unshift(i);
}
console.log(variable1);
> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 6, 3, 2, 6, 7, 2, 9, 1, 5]
Am I only able to timestamp the state of my data at a given point in the run time by creating a new array and pushing in the contents of the previous array, or is there another practice used? Thanks.
Array.from()
const array2 = Array.from(array1)
console.log(array2)
If you don't want this behaviour, you have to make sure to use methods that generate a new array on every mutation of the original array.
To copy an array you can use Array.from(array) or ES6 [...array].
With that knowledge: For array.unshift(e) you can use ES6 array = [...array, e];
let variable1 = [6, 3, 2, 6, 7, 2, 9, 1, 5];
let variable2 = variable1;
for (i = 0; i < 10; i++) {
variable2 = [...variable2, i];
}
console.log('var2', variable1);
console.log('var1', variable2);
Javascript generally is always pass by value, but in the case when the variable refers to an object (including arrays) the "value" is a reference to that object.
When you change the value of a variable, it doesn't change the underlying object or primitive - instead it just points the variable to the new value.
However changing properties on an object (including arrays) will change the underlying object itself.
tl;dr
There is no way to capture the state at a given timepoint without making a complete copy of it.
How to create the copy
Depending on how your data is structured there are multiple ways you could go about to create a clone of it.
If it is just an array of primitives, e.g. an array of numbers / strings, a shallow copy of the array would suffice:
const arr = [1,2,3,"foo"];
// using array spread
const clone1 = [...arr];
// Array.from()
const clone2 = Array.from(arr);
// mapping the array
const clone3 = arr.map(e => e);
// push with spread
const clone4 = [];
clone4.push(...arr);
// good old for loop
const clone5 = [];
for(let i = 0; i < arr.length; i++)
clone5.push(arr[i]);
If you have a deep data structure with nested objects / arrays, you need to do the shallow copying recursively to achieve a deep copy.
However there are already lots of good libraries that can handle these for you, for example lodash:
const value = [{a: 1}, {b: 2}, {c: ["a", "b"]}];
// using lodash _.cloneDeep to get a deep copy
const clone = _.cloneDeep(value);

JS - For Loops Pushing Array

I have an initial array,
I've been trying to change values (orders) by using pop, splice methods inside a for loop and finally I push this array to the container array.
However every time initial array is values are pushed. When I wrote console.log(initial) before push method, I can see initial array has been changed but it is not pushed to the container.
I also tried to slow down the process by using settimeout for push method but this didnt work. It is not slowing down. I guess this code is invoked immediately
I would like to learn what is going on here ? Why I have this kind of problem and what is the solution to get rid of that.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial);
}
return schedulePattern;
}
**Console.log**
(4) [1, 2, 3, 4]
(4) [1, 4, 2, 3]
(4) [1, 3, 4, 2]
(3) [Array(4), Array(4), Array(4)]
0 : (4) [1, 3, 4, 2]
1 : (4) [1, 3, 4, 2]
2 : (4) [1, 3, 4, 2]
length : 3
When you push initial into schedulePattern, it's going to be a bunch of references to the same Array object. You can push a copy of the array instead if you want to preserve its current contents:
schedulePattern.push(initial.slice(0));
Good answer on reference types versus value types here: https://stackoverflow.com/a/13266769/119549
When you push the array to schedulepattern, you are passing a reference to it.
you have to "clone" the array.
use the slice function.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial.slice());
}
return schedulePattern;
}
​
You have to know that arrays are mutable objects. What does it mean? It means what is happening to you, you are copying the reference of the object and modifying it.
const array = [1,2,3]
const copy = array;
copy.push(4);
console.log(array); // [1, 2, 3, 4]
console.log(copy); // [1, 2, 3, 4]
There are a lot of methods in Javascript which provide you the way you are looking for. In other words, create a new array copy to work properly without modify the root.
const array = [1,2,3]
const copy = Array.from(array);
copy.push(4);
console.log(array); // [1, 2, 3]
console.log(copy); // [1, 2, 3, 4]
I encourage you to take a look at Array methods to increase your knowledge to take the best decision about using the different options you have.

pushing an array into another array

I have the following arrays.
var arr1=[1,2,3];
var arr2=[4,5,6];
var arr3=[];
How would I push arr1 and arr2 into arr3 such that the result is:
arr3=[[1,2,3],[4,5,6]];
not
arr3=[1,2,3,4,5,6];
which is produced when using the .concat method.
var arr1=[1,2,3];
var arr2=[4,5,6];
var arr3=[];
arr3.push(arr1,arr2);
console.log(arr3);
var arr1=[1,2,3];
var arr2=[4,5,6];
var arr3 = [arr1,arr2];
console.log(arr3);
arr3.push(arr1);
arr3.push(arr2);
will do the work.
Anyway just follow http://www.w3schools.com/jsref/jsref_push.asp for further clarifications.
Hope this helps.
You can use push() like in the other answers or this:
var arr1 = [1,2,3];
var arr2 = [4,5,6];
var arr3 = [arr1, arr2];
console.log(arr3);
As others users have posted, the solution at your problem is use .push() method, but i would try to give you more information about these two methods.
I suggest you to read this article, is very useful for me; I recap the main information for you below.
PUSH METHOD
Push method is used when you want add one or more elements, that were input arguments, to the array that invoked the method.
A simply example is the following:
var first_array = [1, 2, 3];
var result = first_array.push(4, 5, 6);
console.log(result); // 6
console.log(first_array ); // [1, 2, 3, 4, 5, 6]
The one thing is not immediately apparent is that it returns the length of the array after adding the new values, not the modified array. (check third line: console.log(result);)
CONCAT METHOD
While push alters the array that invoked it, concat returns a new array with the original array joined with the array/s or value/s that were provided as arguments.
There are a couple of the things to note about concat and how it creates the returned array.
Both strings and numbers are copied into the array, which means that they if the original value is changed, the value in the new array will be unaffected.
This is not true for objects.
Instead of copying objects into the new array, the references are copied instead. This means that if the values of objects change in one array, they will also be changed in the other array, as they are references to the objects not unique copies.
A simply example is the following:
var test = [1, 2, 3]; // [1, 2, 3]
var example = [{ test: 'test value'}, 'a', 'b', 4, 5];
var concatExample = test.concat(example); // [1, 2, 3, { test: 'test value'}, 'a', 'b', 4, 5]
example[0].test = 'a changed value';
console.log(concatExample[3].test); // Object { test: "a changed value"}
example[1] = 'dog';
console.log(concatExample[4]); // 'a'
I have created a fiddle for you with this example.
I hope that will be helpful

Compare values in two arrays using Iterator in Javascript?

I know how to compare values in two arrays using 2 for loops however I was looking for something a bit more sophisticated like creating an iterator to iterate through one of the arrays and passing the other array to mapmethod . Is that even possible?
I'm doing a small program for class which takes an array and x arguments and I currently have extracted the values from the arguments.
function dest(arr){
var args =[];
for(var i = 1; i < arguments.length; i++){
args.push(arguments[i]);
}
return args;
}
console.log(dest([1, 2, 3, 4], 4, 4));
Now, how could I do the iterator part to compare the values inside arr and args? Thanks for the help.
The result should be the results that match from both arr and args.
You can use the built in filter method
var arr = [2, 3, 4, 5, 6];
var args = [3, 5, 6, 7];
var result = arr.filter(function(element) {
return args.indexOf(element) > -1;
});
This will filter out all the elements out that are not present in both arrays. The result is a new array that contains only the matching values [3, 5, 6].

How to replace elements in array with elements of another array

I want to replace elements in some array from 0 element, with elements of another array with variable length. Like:
var arr = new Array(10), anotherArr = [1, 2, 3], result;
result = anotherArr.concat(arr);
result.splice(10, anotherArr.length);
Is there some better way?
You can use the splice method to replace part of an array with items from another array, but you have to call it in a special way as it expects the items as parameters, not the array.
The splice method expects parameters like (0, anotherArr.Length, 1, 2, 3), so you need to create an array with the parameters and use the apply method to call the splice method with the parameters:
Array.prototype.splice.apply(arr, [0, anotherArr.length].concat(anotherArr));
Example:
var arr = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
var anotherArr = [ 1, 2, 3 ];
Array.prototype.splice.apply(arr, [0, anotherArr.length].concat(anotherArr));
console.log(arr);
Output:
[ 1, 2, 3, 'd', 'e', 'f', 'g', 'h', 'i', 'j']
Demo: http://jsfiddle.net/Guffa/bB7Ey/
In ES6 with a single operation, you can do this to replace the first b.length elements of a with elements of b:
let a = [1, 2, 3, 4, 5]
let b = [10, 20, 30]
a.splice(0, b.length, ...b)
console.log(a) // -> [10, 20, 30, 4, 5]
It could be also useful to replace the entire content of an array, using a.length (or Infinity) in the splice length:
let a = [1, 2, 3, 4, 5]
let b = [10, 20, 30]
a.splice(0, a.length, ...b)
// or
// a.splice(0, Infinity, ...b)
console.log(a) // -> [10, 20, 30], which is the content of b
The a array's content will be entirely replaced by b content.
Note 1: in my opinion the array mutation should only be used in performance-critical applications, such as high FPS animations, to avoid creating new arrays. Normally I would create a new array maintaining immutability.
Note 2: if b is a very large array, this method is discouraged, because ...b is being spread in the arguments of splice, and there's a limit on the number of parameters a JS function can accept. In that case I encourage to use another method (or create a new array, if possible!).
In ES6, TypeScript, Babel or similar you can just do:
arr1.length = 0; // Clear your array
arr1.push(...arr2); // Push the second array using the spread opperator
Simple.
For anyone looking for a way to replace the entire contents of one array with entire contents of another array while preserving the original array:
Array.prototype.replaceContents = function (array2) {
//make a clone of the 2nd array to avoid any referential weirdness
var newContent = array2.slice(0);
//empty the array
this.length = 0;
//push in the 2nd array
this.push.apply(this, newContent);
};
The prototype function takes an array as a parameter which will serve as the new array content, clones it to avoid any weird referential stuff, empties the original array, and then pushes in the passed in array as the content. This preserves the original array and any references.
Now you can simply do this:
var arr1 = [1, 2, 3];
var arr2 = [3, 4, 5];
arr1.replaceContents(arr2);
I know this is not strictly what the initial question was asking, but this question comes up first when you search in google, and I figured someone else may find this helpful as it was the answer I needed.
You can just use splice, can add new elements while removing old ones:
var arr = new Array(10), anotherArr = [1, 2, 3];
arr.splice.apply(arr, [0, anotherArr.length].concat(anotherArr))
If you don't want to modify the arr array, you can use slice that returns a shallow copy of the array:
var arr = new Array(10), anotherArr = [1, 2, 3], result = arr.slice(0);
result.splice.apply(result, [0, anotherArr.length].concat(anotherArr));
Alternatively, you can use slice to cut off the first elements and adding the anotherArr on top:
result = anotherArr.concat(arr.slice(anotherArr.length));
I'm not sure if it's a "better" way, but at least it allows you to choose the starting index (whereas your solution only works starting at index 0). Here's a fiddle.
// Clone the original array
var result = arr.slice(0);
// If original array is no longer needed, you can do with:
// var result = arr;
// Remove (anotherArr.length) elements starting from index 0
// and insert the elements from anotherArr into it
Array.prototype.splice.apply(result, [0, anotherArr.length].concat(anotherArr));
(Damnit, so many ninjas. :-P)
You can just set the length of the array in this case. For more complex cases see #Guffa's answer.
var a = [1,2,3];
a.length = 10;
a; // [1, 2, 3, undefined x 7]

Categories

Resources