How do you clone an Array of Objects in Javascript?

How do you clone an Array of Objects in Javascript?

…where each object also has references to other objects within the same array?
When I first came up with this problem I just though of something like
var clonedNodesArray = nodesArray.clone()

would exist and searched for info on how to clone objects in javascript. I did find a question on StackOverflow (answered by the very same @JohnResig) and he pointed out that with jQuery you could do
var clonedNodesArray = jQuery.extend({}, nodesArray);

to clone an object. I tried this though, this only copies the references of the objects in the array. So if I
nodesArray[0].value = “red”
clonedNodesArray[0].value = “green”

the value of both nodesArray[0] and clonedNodesArray[0] will turn out to be “green”. Then I tried
var clonedNodesArray = jQuery.extend(true, {}, nodesArray);

which deep copies an Object, but I got “too much recursion” and “control stack overflow” messages from both Firebug and Opera Dragonfly respectively.
How would you do it? Is this something that shouldn’t even be done? Is there a reusable way of doing this in Javascript?

Solutions/Answers:

Solution 1:

The issue with your shallow copy is that all the objects aren’t cloned. While the references to each object are unique in each array, once you ultimately grab onto it you’re dealing with the same object as before. There is nothing wrong with the way you cloned it… the same result would occur using Array.slice().

The reason your deep copy is having problems is because you’re ending up with circular object references. Deep will go as deep as it can go, and if you’ve got a circle, it’ll keep going infinitely until the browser faints.

If the data structure cannot be represented as a directed acyclic graph, then I’m not sure you’re going to be able to find an all-purpose method for deep cloning. Cyclic graphs provide many tricky corner cases, and since it’s not a common operation I doubt anyone has written a full solution (if it’s even possible – it might not be! But I have no time to try to write a rigorous proof now.). I found some good comments on the issue on this page.

If you need a deep copy of an Array of Objects with circular references I believe you’re going to have to code your own method to handle your specialized data structure, such that it is a multi-pass clone:

  1. On round one, make a clone of all objects that don’t reference other objects in the array. Keep a track of each object’s origins.
  2. On round two, link the objects together.

Solution 2:

As long as your objects contain JSON-serializable content (no functions, no Number.POSITIVE_INFINITY, etc.) there is no need for any loops to clone arrays or objects. Here is a pure vanilla one-line solution.

var clonedArray = JSON.parse(JSON.stringify(nodesArray))

To summarize the comments below, the primary advantage of this approach is that it also clones the contents of the array, not just the array itself. The primary downsides are its limit of only working on JSON-serializable content, and it’s performance (which is significantly worse than a slice based approach).

Solution 3:

I solved cloning of an array of objects with Object.assign

const newArray = myArray.map(a => Object.assign({}, a));

or even shorter with spread syntax

const newArray = myArray.map(a => ({...a}));

Solution 4:

If all you need is a shallow copy, a really easy way is:

new_array = old_array.slice(0);

Solution 5:

Best and most up to date way to do this clone is as follows:

Using the “…” ES6 spread operator.

Here’s the most simple Example:

var clonedObjArray = [...oldObjArray];

This way we spread the array into individual values and put it in a new array with the [] operator.

Here’s a longer example that shows the different ways it works:

let objArray = [ {a:1} , {b:2} ];

let refArray = objArray; // this will just point to the objArray
let clonedArray = [...objArray]; // will clone the array

console.log( "before:" );
console.log( "obj array" , objArray );
console.log( "ref array" , refArray );
console.log( "cloned array" , clonedArray );

objArray[0] = {c:3};

console.log( "after:" );
console.log( "obj array" , objArray ); // [ {c:3} , {b:2} ]
console.log( "ref array" , refArray ); // [ {c:3} , {b:2} ]
console.log( "cloned array" , clonedArray ); // [ {a:1} , {b:2} ]

Solution 6:

This works for me:

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend({}, obj);
                  });

And if you need deep copy of objects in array:

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend(true, {}, obj);
                  });