Copying an array of objects into another array in javascript (Deep Copy) - javascript

Copying an array of objects into another array in javascript using slice(0) and concat() doesnt work.
I have tried the following to test if i get the expected behaviour of deep copy using this. But the original array is also getting modified after i make changes in the copied array.
var tags = [];
for(var i=0; i<3; i++) {
tags.push({
sortOrder: i,
type: 'miss'
})
}
for(var tag in tags) {
if(tags[tag].sortOrder == 1) {
tags[tag].type = 'done'
}
}
console.dir(tags)
var copy = tags.slice(0)
console.dir(copy)
copy[0].type = 'test'
console.dir(tags)
var another = tags.concat()
another[0].type = 'miss'
console.dir(tags)
How can i do a deep copy of a array into another, so that the original array is not modified if i make a change in copy array.

Try
var copy = JSON.parse(JSON.stringify(tags));

Try the following
// Deep copy
var newArray = jQuery.extend(true, [], oldArray);
For more details check this question out What is the most efficient way to deep clone an object in JavaScript?

As mentioned Here .slice(0) will be effective in cloning the array with primitive type elements. However in your example tags array contains anonymous objects. Hence any changes to these objects in cloned array are reflected in tags array.
#dangh's reply above derefences these element objects and create new ones.
Here is another thread addressing similar situation

A nice way to clone an array of objects with ES6 is to use spread syntax:
const clonedArray = [...oldArray];
MDN

Easiest and the optimistic way of doing this in one single line is using Underscore/Lodash
let a = _.map(b, _.clone)

You just need to use the '...' notation.
// THE FOLLOWING LINE COPIES all elements of 'tags' INTO 'copy'
var copy = [...tags]
When you have an array say x, [...x] creates a new array with all the values of x. Be careful because this notation works slightly differently on objects. It splits the objects into all of its key, value pairs. So if you want to pass all the key value pairs of an object into a function you just need to pass function({...obj})

Same issue happen to me. I have data from service and save to another variable. When ever I update my array the copied array also updated. old code is like below
//$scope.MyData get from service
$scope.MyDataOriginal = $scope.MyData;
So when ever I change $scope.MyData also change $scope.MyDataOriginal.
I found a solution that angular.copy right code as below
$scope.MyDataOriginal = angular.copy($scope.MyData);

I know that this is a bit older post but I had the good fortune to have found a decent way to deep copy arrays, even those containing arrays, and objects, and even objects containing arrays are copied... I only can see one issue with this code is if you don't have enough memory I can see this choking on very large arrays of arrays and objects... But for the most part it should work. The reason that I am posting this here is that it accomplishes the OP request to copy array of objects by value and not by reference... so now with the code (the checks are from SO, the main copy function I wrote myself, not that some one else probably hasn't written before, I am just not aware of them)::
var isArray = function(a){return (!!a) && (a.constructor===Array);}
var isObject = function(a){return (!!a) && (a.constructor===Object);}
Array.prototype.copy = function(){
var newvals=[],
self=this;
for(var i = 0;i < self.length;i++){
var e=self[i];
if(isObject(e)){
var tmp={},
oKeys=Object.keys(e);
for(var x = 0;x < oKeys.length;x++){
var oks=oKeys[x];
if(isArray(e[oks])){
tmp[oks]=e[oks].copy();
} else {
tmp[oks]=e[oks];
}
}
newvals.push(tmp);
} else {
if(isArray(e)){
newvals.push(e.copy());
} else {
newvals.push(e);
}
}
}
return newvals;
}
This function (Array.prototype.copy) uses recursion to recall it self when an object or array is called returning the values as needed. The process is decently speedy, and does exactly what you would want it to do, it does a deep copy of an array, by value... Tested in chrome, and IE11 and it works in these two browsers.

The way to deeply copy an array in JavaScript with JSON.parse:
let orginalArray=
[
{firstName:"Choton", lastName:"Mohammad", age:26},
{firstName:"Mohammad", lastName:"Ishaque", age:26}
];
let copyArray = JSON.parse(JSON.stringify(orginalArray));
copyArray[0].age=27;
console.log("copyArray",copyArray);
console.log("orginalArray",orginalArray);

For this i use the new ECMAScript 6 Object.assign method :
let oldObject = [1,3,5,"test"];
let newObject = Object.assign({}, oldObject)
the first argument of this method is the array to be updated,
we pass an empty object because we want to have a completely new object,
also you can add other objects to be copied too :
let newObject = Object.assign({}, oldObject, o2, o3, ...)

Related

js - When pushing an array A into a multidimensional array B, it is overwriting the previous array already on A

Here's an example from the browser console
a=[[1,2,3],[3,2,1]]
b=[]
b.push(a)
a[0][0]=9
b.push(a)
In that case, I was expecting b to be
[[[1,2,3],[3,2,1]],[[9,2,3],[3,2,1]]]
But it will be
[[[9,2,3],[3,2,1]],[[9,2,3],[3,2,1]]]
I'm attaching a screenshot to better show the results in the browser:
browsers console with the same code as above and output
Any ideas on why this is happening and how to get to my expecting results?
For the language itself, there are no multidimentional arrays - there are another arrays inside an array. It doesn't matter if contains arrays, plain objects, functions, or primitives. For primitives, their values will be copied. Otherwise, the references to objects will be copied.
You can do it via pushing the copies of inner arrays of array a with the help of map and spread operator(...).
var a=[[1,2,3],[3,2,1]];
var b=[];
b.push(a.map(x=>[...x]));
a[0][0]=9;
b.push(a);
console.log(b);
Ok, based on all the comments here, specially #code 's one, and also another post (How do I pass the value instead of the reference of an array?) I was able to achieve the results by using this:
b.push([]);
for(line of a){
b[b.length-1].push(a.slice(0));
}
EDIT
Abhijeet's suggestion also works flawlessly:
b.push(a.map(x=>[...x]));
In JavaScript, objects are passed by reference, meaning "copies" of it aren't copies. They point to the same address in RAM, so mutating one reference mutates 'em all.
To apply to this case, if you didn't already know, arrays are special types of objects. To prove this you can run typeof [] in your browser console. That means that arrays inherit the "pass-by-reference" behavior, meaning if you do let c = a then run c[0] = 1, a will be changed as well and vice versa.
To solve this issue you can use Array.prototype.slice:
const a = [[1, 2, 3], [3, 2, 1]];
const b = [];
Array.prototype.pushWithoutReference = function(add) {
for(let i = 0; i < add.length; i++) {
this[this.length] = add[i].slice(0);
}
}
b.pushWithoutReference(a);
a[0][0] = 9;
b.pushWithoutReference(a);

JavaScript scoping (copying one array to another)

In this code... mapObj.fetchTimeObjs should NOT change right?!?!
Somehow mapObj.fetchTimeObjs gets changed when this function is run:
function clockErasePast(){
var now = new Date().getTime();
var tmpFetchTimeObjs = [];
for(var i =0; i<mapObj.fetchTimeObjs.length; i++){
tmpFetchTimeObjs.push(mapObj.fetchTimeObjs[i]);
if(mapObj.fetchTimeObjs[i].start < now){tmpFetchTimeObjs[i].start = now;}
}
return tmpFetchTimeObjs;
}
tmpFetchTimeObjs[i]
will contain only reference to the mapObj.fetchTimeObjs[i].
If you will change tmpFetchTimeObjs[i], the mapObj.fetchTimeObjs[i] will be changed, because you will have only one object which has two references. And if it will changed from one reference, it will be changed for the second reference too.
Let's consider an object which has two references. Here I change the object from one reference, and get the update for the second reference, because they refer to the same object.
var objA = { name: 'Bob', age: 25};
var objB = objA;
objB.age = 30;
console.log(objA.age);
To get independent object you need to create them. You can use Object.assign() function, which will copy any enumerable properties from into the destination(first parameter) object and returns it.
You can create with
var obj = Object.assign({}, mapObj.fetchTimeObjs[i]);
tmpFetchTimeObjs.push(obj);
The objects you push to the new array, are the same as the original array has, so if you mutate them, that mutation is visible to both arrays. This is what is called doing a shallow copy.
You could make a copy at one deeper level, where you would also create new objects and copy the original object's properties (like start) into those new objects. This you can easily do with Object.assign:
tmpFetchTimeObjs.push(Object.assign({}, mapObj.fetchTimeObjs[i]));
If these objects themselves have nested objects, then the problem you had would occur at deeper levels. If this is an issue, then you should look to deep clone solutions, like provided in this Q&A.
You need to clone the TimeObjs, since variables only keep the reference to the TimeObjs. We don't know the structure of your TimeObjs, so if the TimeObjs doesn't contain other objects, Object.assign() will work, otherwise maybe you need a deep clone method, such as jQuery.extend().

How to update object properties within a loop?

I have found a behavior I did not expect when trying to use a loop in order to change the value set for a property in an object.
Basically, I declare my object outside the loop.
Then I loop on an array of numeric values, which values are used to update the object property.
Inside the loop, I store the current object state inside an external array.
The result is that instead of having an array containing a series of objects with different numeric values, I end up having the same numeric values in each object stored.
Here is the fiddle http://jsfiddle.net/fAypL/1/
jQuery(function(){
var object_container = [];
var numeric_values = [1, 2 , 3, 4];
var my_object = {};
jQuery.each(numeric_values, function(index, value){
my_object['value'] = value;
object_container.push(my_object);
});
jQuery.each(object_container, function(index, value){
jQuery('#content').prepend(value['value']);
});
});
I would expect to get 1 2 3 4 as values stored in each object, however, what I get is 4 4 4 4, which does not make sense to me.
Any hint on this behavior is more than welcome, thanks
When your code calls .push() and passes my_object, what's being passed is a reference to the object. No copy is made.
Thus, you've pushed four references to the exact same object into the array.
JavaScript objects always participate in expressions in the form of references. There's no other way to deal with objects. Thus when you create a variable, and set its value to be an object, you're really setting its value to be a reference to the object. Same with parameter passing, and anywhere else an object can appear in an expression.
In this case, you can create new objects pretty easily; just dispense with my_object and push a fresh one on each iteration:
object_container.push( { value: value } );
You are not creating a new object each time around the loop - you are just updating the same existing object and pushing references of that to the object array. To create a new object you want to do something like:
my_object = { 'value': value };
object_container.push(my_object);
In this case you now will get something more like what you were looking for. See the updated fiddle here: http://jsfiddle.net/fAypL/2/.
Best of luck!
One more thought (Clone!) - If you are really tied to using the same object each time, just clone the object before you add to the array. There is a great solution for that here.
You are using jQuery so if what you want is to merge without effecting the original look at :
var both_obj = $.extend( {}, default_obj , adding_obj );
This will leave your original object changed, also good to use for a copy.
jquery docs - extend()
An alternate version is to use an object with a constructor and the new keyword:
var object_container = [];
var numeric_values = [1, 2 , 3, 4];
function MyObject(value)
{
this.value = value;
}
jQuery.each(numeric_values, function(index, value){
object_container.push(new MyObject(value));
});
jQuery.each(object_container, function(index, value){
jQuery('#content').prepend(value['value']);
});
Fiddle

javascript array push value of object

I am trying to create an array of objects, however when I am pushing onto my array it is adding a reference to the object rather than copying the values.
var nestedOrgList = [];
var tempTopOrg = {};
var i = 0;
while (typeof divList[i] !== 'undefined') {
tempTopOrg.org = divList[i++]; // increment i after we assign this
tempTopOrg.suborgs = [];
while ($(divList[i]).has('.expand').length < 1 && i < divList.length) {
tempTopOrg.suborgs.push(divList[i++]);
}
nestedOrgList.push(tempTopOrg);
};
Is there a better way to do this? Or do I need to do a manual copy?
nestedOrgList[insertIndex].org = tempTopOrg.org;
// etc..
insertIndex++;
You can check the following answer
How do I correctly clone a JavaScript object?
The JSperf
http://jsperf.com/cloning-an-object/82
definitely JavaScript should have a way natively to copy references.
A common method if speed is not a critical goal is to encode/decode the object using JSON:
var json = JSON.stringify(tempTopOrg);
nestedOrgList.push( JSON.parse(json) );
javascript passes objects and arrays by reference, so you will have to make a copy before pushing,
myarray.push(JSON.parse(JSON.stringify(obj)));
is quick and dirty and probably has performance issues,
this question tries to tackle cloning of objects.
to make a deep copy use
var newcopy = temp.slice(0);
and to filter undefined and null values use
newcopy = newcopy.filter(function(e){ return e });

How to merge these arrays/json objects?

I am a bit confused at this point on what is an object, what is an array, and what is a JSON. Can someone explain the differences in syntax between the two? and how to add items to each, how to merge each type, and such? I am trying to get this function to take the new information from a JSON object (I think) and merge it with some new information. This information will then be passed to a PHP script to be processed.
Here is the console output:
{"public":{"0":["el29t7","3bmGDy"]}}
{"public":"[object Object][object Object]"}
Here is the JS I am using:
/* Helper function to clean up any current data we have stored */
function insertSerializedData(ids, type) {
// Get anything in the current field
current_data = $('#changes').val();
if (!current_data) {
var data = {};
data[index++] = ids;
var final_data = {};
final_data[type] = data;
$('#changes').val(JSON.stringify(final_data));
} else {
current_data = JSON.parse(current_data);
var data = {};
data[index++] = ids;
// Does the index exist?
if (type in current_data) {
var temp_data = current_data[type];
current_data[type] = temp_data + data;
} else {
current_data[type] = data;
}
//var extra_data = {};
//extra_data[type] = data;
//$.merge(current_data, extra_data);
$('#changes').val(JSON.stringify(current_data));
}
console.log($('#changes').val());
}
The idea is if the key (public, or whatever other ones) doesn't exist yet, then to make it point to an array of arrays. If it does exist though, then that of array of arrays need to be merged with a new array. For instance:
If I have
{"public":{"0":["el29t7","3bmGDy"]}}
and I want to merge it with
["aj19vA", "jO71Ba"]
then final result would be:
{"public":{"0":["el29t7","3bmGDy"], "1":["aj19vA", "jO71Ba"]}}
How can i go about doing this? Thanks
Excellent two-part question. Overall, the second question is non-trivial because of the complexity of the first.
Question 1:
what is an object, what is an array, and what is a JSON. Can someone
explain the differences in syntax between the two?
Question 2:
and how to add items to each,
Question 3:
how to merge each type, and such?
Answer 1:
This is a common stumbling point because, JavaScript is more flexible than one might initially expect. Here is the curve.
In JavaScript everything is an object.
So here is the code for each:
//What is an object?
var obj = { };
var obj2 = { member:"value", myFunction:function(){} }
Above is an empty object. Then another object with a variable and a function.
They are called object-literals.
//What is an array
var array1 = [ ] ;
var array2 = [0,1,2,3,4];
Above is an empty array. Then another array with five Integers.
Here is the curve that causes confusion.
//Get elements from each of the prior examples.
var x = obj2["member"];
var y = array2[1];
What??? Both Object and Array are accessing values with a bracket?
This is because both are objects. This turns out to be a nice flexibility for writing advanced code. Arrays are objects.
//What is JSON?
JSON stands for JavaScript Object Notiation. As you might have guessed. Everything is an object... It is also an { }; But it is different because - it is used to transfer data to - and - from JavaScript, not actually used (commonly) in JavaScript. It is a file transfer format.
var JSONObject = {"member":"value"};
The only difference to the prior example is quotes. Essentially we are wrapping the object literal as a string so that it can be transferred to a server, or back, and it can be reinterpreted, very easily. Better than XML - because it does not have to be custom-parsed. Just call, stringify() or ParseJSON(). Google it. The point is... JSON can be converted into an object-literal JS object, and JS object-literals can be converted into JSON, for transfer to a server or a CouchDB database, for example.
Sorry for the tangent.
Answer 2:
How to add an item to each? Here is where the curve stops being a nuisance, and starts being awesome! Because everything is an object, it is all just about the same.
//Add to an object
var obj {member1:"stringvalue"}
obj.member2 = "addme"; //That is it!
//Add to an array
var array1 [1,2,3,4,5];
array1[0] = "addme";
array[6] = null;
//We shouldn't mix strings, integers, and nulls in arrays, but this isn't a best-practice tutorial.
Remember the JS object syntax and you may start to see a whole new flexible world of objects open up. But it may take a bit.
Answer 3: Ah, yeah... how to merge.
There are seriously (very many) ways to merge two arrays. It depends on exactly what you need. Sorted, Duplicated, Concatenated... there are a few.
Here is the answer!
UPDATE: How to make a beautiful multiple dimensional array.
//Multiple Dimension Array
var array1 = [1,2,3];
var array2 = [3,4];
var arraysinArray = [array1,array2]; //That is it!
Here is the curve again, this could be in an object:
var obj{
array1:[1,2,3],
array2:[3,4]
}
JavaScript is powerful stuff, stick with it; it gets good. : )
Hope that helps,
All the best!
Nash
In this case, think of a JavaScript's object literal {} as being like PHP's associative array.
Given that, an "array of arrays" actually looks like this (using your above desired output):
{public: [["el29t7","3bmGDy"], ["aj19vA", "jO71Ba"]]}
So here we have an object literal with a single property named "public" whose value is a 2-dimensional array.
If we assign the above to a variable we can then push another array onto "public" like this:
var current_data = {public: [["el29t7","3bmGDy"], ["aj19vA", "jO71Ba"]]};
// Using direct property access
current_data.public.push(["t9t9t9", "r4r4r4"]);
// Or using bracket notation
current_data["public"].push(["w2w2w2", "e0e0e0"]);
current_data's value is now:
{public: [
["el29t7","3bmGDy"],
["aj19vA", "jO71Ba"],
["t9t9t9", "r4r4r4"],
["w2w2w2", "e0e0e0"]
]}
So now "public" is an array whose length is 4.
current_data.public[0]; // ["el29t7","3bmGDy"]
current_data.public[1]; // ["aj19vA", "jO71Ba"]
current_data.public[2]; // ["t9t9t9", "r4r4r4"]
current_data.public[3]; // ["w2w2w2", "e0e0e0"]
MDN has very good documentation on Array for insight on other functions you might need.
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array
First is an object, that contains array, second is an array.
DEMO showing display output http://jsfiddle.net/GjQCV/
var object={"public":{"0":["el29t7","3bmGDy"]}};
var arr=["aj19vA", "jO71Ba"] ;
/* use object notation to add new property and value which is the array*/
object.public[1]=arr;
It'd be much more natural if {"0": ...} were a true array rather than an object, but anyway:
function maxKey(b) {
var max;
for( var key in b )
var max = key;
return max;
}
function merge(a,b) {
for( var key in a ) {
b[key] = b[key] ? (b[key][maxKey(b)+1]=a[key], b[key]) : a[key];
}
return b;
}
Note that this assumes you would insert at the next integer index
Arrays are a particular kind of Javascript object
JSON is a way of representing Javascript objects (and as such can represent arrays and more)
Objects are much more general, and can be simple objects that can be represented as JSON, or can contain functions and prototypes.
So, this is not an array of arrays (you would access items using JSON notation like myobj["0"]):
{"0":["el29t7","3bmGDy"], "1":["aj19vA", "jO71Ba"]}
This is an array of arrays, which means you can use the push method to add an item, and access items using array notation like myobj[0]:
[ ["el29t7","3bmGDy"], ["aj19vA", "jO71Ba"] ]
It seems like the structure you want is something like this:
var myobj = { "public": [ ["key", "value"], ["key", "value"] ] }
Then if you want to add/merge new items, you'd write this:
if (myobj["public"] != null) {
myobj["public"].push(["newkey", "newval"]);
} else {
myobj["public"] = ["newkey", "newval"];
}

Categories