How do you clone an Array of Objects in Javascript?

0 votes
asked Feb 28, 2009 by wallyqs

...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?

25 Answers

0 votes
answered Jan 28, 2009 by markt

Array.slice can be used to copy an array or part of an array.. http://www.devguru.com/Technologies/Ecmascript/Quickref/Slice.html This would work with strings and numbers .. - changing a string in one array would not affect the other - but objects are still just copied by reference so changes to referenced objects in one array would have an affect on the other array.

Here is an example of a JavaScript undo manager that could be useful for this :http://www.ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx

0 votes
answered Feb 28, 2009 by daniel-lew

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.
0 votes
answered Jan 4, 2010 by elsereturn
$.evalJSON($.toJSON(origArray));
0 votes
answered Jan 31, 2010 by george

I may have a simple way to do this without having to do painful recursion and not knowing all the finer details of the object in question. Using jQuery, simply convert your object to JSON using the jQuery $.toJSON(myObjectArray), then take your JSON string and evaluate it back to an object. BAM! Done, and done! Problem solved. :)

var oldObjArray = [{ Something: 'blah', Cool: true }];
var newObjArray = eval($.toJSON(oldObjArray));
0 votes
answered Feb 9, 2010 by leopd

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

new_array = old_array.slice(0);
0 votes
answered Jan 2, 2012 by lujan99

with jQuery:

var target= [];
$.each(source, function() {target.push( $.extend({},this));});
0 votes
answered Jan 4, 2012 by brak

I was pretty frustrated by this problem. Apparently the problem arises when you send in a generic Array to the $.extend method. So, to fix it, I added a little check, and it works perfectly with generic arrays, jQuery arrays, and any objects.

jQuery.extend({
    deepclone: function(objThing) {
        // return jQuery.extend(true, {}, objThing);
        /// Fix for arrays, without this, arrays passed in are returned as OBJECTS! WTF?!?!
        if ( jQuery.isArray(objThing) ) {
            return jQuery.makeArray( jQuery.deepclone($(objThing)) );
        }
        return jQuery.extend(true, {}, objThing);
    },
});

Invoke using:

var arrNewArrayClone = jQuery.deepclone(arrOriginalArray);
// Or more simply/commonly
var arrNewArrayClone = $.deepclone(arrOriginalArray);
0 votes
answered Jan 10, 2012 by krupar

forget eval() (is the most misused feature of JS and makes the code slow) and slice(0) (works for simple data types only)

This is the best solution for me:

Object.prototype.clone = function() {
  var myObj = (this instanceof Array) ? [] : {};
  for (i in this) {
    if (i != 'clone') {
        if (this[i] && typeof this[i] == "object") {
          myObj[i] = this[i].clone();
        } else 
            myObj[i] = this[i];
        } 
    }
  return myObj;
};
0 votes
answered Jan 20, 2012 by stef

JQuery extend is working fine, just you need to specify that you are cloning an array rather than an object (note the [] instead of {} as parameter to the extend method):

var clonedNodesArray = jQuery.extend([], nodesArray);
0 votes
answered Jan 22, 2012 by viliks

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);
                  });
Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...