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

This question is related to javascript

The answer is


As Daniel Lew mentioned, cyclic graphs have some problems. If I had this problem I'd either add special clone() methods to the problematic objects or remember which objects I've already copied.

I'd do it with a variable copyCount which increases by 1 every time you copy in your code. An object that has a lower copyCount than the current copy-process is copied. If not, the copy, that exists already, should be referenced. This makes it necessary to link from the original to its copy.

There is still one problem: Memory. If you have this reference from one object to the other, it's likely that the browser can't free those objects, as they are always referenced from somewhere. You'd have to make a second pass where you set all copy-references to Null. (If you do this, you'd not have to have a copyCount but a boolean isCopied would be enough, as you can reset the value in the second pass.)


I think managed to write a generic method of deep cloning any JavaScript structure mainly using Object.create which is supported in all modern browsers. The code is like this:

function deepClone (item) {
  if (Array.isArray(item)) {
    var newArr = [];

    for (var i = item.length; i-- !== 0;) {
      newArr[i] = deepClone(item[i]);
    }

    return newArr;
  }
  else if (typeof item === 'function') {
    eval('var temp = '+ item.toString());
    return temp;
  }
  else if (typeof item === 'object')
    return Object.create(item);
  else
    return item;
}

function deepCloneArray(array) {
    return Array.from(Object.create(array));
}

Here is a my solution, It works for an array of objects or Map. This solution keeps the methods also.

A deep copy means actually creating a new array and copying over the values, since whatever happens to it will never affect the origin one.

This is the best solution for me:

deepCopy(inputObj: any) {
    var newObj = inputObj;
    if (inputObj && typeof inputObj === "object") {
        newObj = Object.prototype.toString.call(inputObj) === "[object Array]" ? [] : {};
        for (var i in inputObj) {
            newObj[i] = this.deepCopy(inputObj[i]);
        }

        //For maps
        if(Object.prototype.toString.call(inputObj) === "[object Map]"){
            newObj = new Map;
            inputObj.forEach((v,k) =>{
                newObj.set(k,this.deepCopy(v));
            });
        }
    }
    return newObj;
}

$.evalJSON($.toJSON(origArray));

We can invent a simple recursive Array method to clone multidimensional arrays. While the objects within the nested arrays keep their reference to the corresponding objects in the source array, arrays won't.

_x000D_
_x000D_
Array.prototype.clone = function(){_x000D_
  return this.map(e => Array.isArray(e) ? e.clone() : e);_x000D_
};_x000D_
_x000D_
var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],_x000D_
    brr = arr.clone();_x000D_
brr[4][2][1] = "two";_x000D_
console.log(JSON.stringify(arr));_x000D_
console.log(JSON.stringify(brr));
_x000D_
_x000D_
_x000D_


If you only need a shallow clone, the best 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:

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


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

Map will create new array from the old one (without reference to old one) and inside the map you create new object and iterate over properties (keys) and assign values from old Array object to coresponding properties to the new object.

This will create exactly the same array of objects.

let newArray = oldArray.map(a => {
               let newObject = {};
               Object.keys(a).forEach(propertyKey => {
                    newObject[propertyKey] = a[propertyKey];
               });
               return newObject ;
});

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

new_array = old_array.slice(0);

The following code will perform recursively a deep copying of objects and array:

function deepCopy(obj) {
if (Object.prototype.toString.call(obj) === '[object Array]') {
    var out = [], i = 0, len = obj.length;
    for ( ; i < len; i++ ) {
        out[i] = arguments.callee(obj[i]);
    }
    return out;
}
if (typeof obj === 'object') {
    var out = {}, i;
    for ( i in obj ) {
        out[i] = arguments.callee(obj[i]);
    }
    return out;
}
return obj;
}

Source


Depending if you have Underscore or Babel here is a Benchmark of the different way of deep cloning an array.

https://jsperf.com/object-rest-spread-vs-clone/2

Look like babel is the fastest.

var x = babel({}, obj)

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

with jQuery:

var target= [];
$.each(source, function() {target.push( $.extend({},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 update, we pass an empty object because we want to have a new object.

we can also use this syntax, which is the same but shorter :

let newObject = [...oldObject];

In JavaScript, array and object copy change the origin values, so Deep copy is the solution for this.

A deep copy means actually creating a new array and copying over the values, since whatever happens to it will never affect the origin one.

JSON.parse and JSON.stringify is the best and simple way to Deep copy. The JSON.stringify() method converts a JavaScript value to a JSON string.The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string.

//Deep Clone

let a = [{ x:{z:1} , y: 2}];
let b = JSON.parse(JSON.stringify(a));
b[0].x.z=0

console.log(JSON.stringify(a)); //[{"x":{"z":1},"y":2}]
console.log(JSON.stringify(b)); // [{"x":{"z":0},"y":2}]

For more details: Read Here


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

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


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

       var game_popularity = [
            { game: "fruit ninja", popularity: 78 },
            { game: "road runner", popularity: 20 },
            { game: "maze runner", popularity: 40 },
            { game: "ludo", popularity: 75 },
            { game: "temple runner", popularity: 86 }
        ];
        console.log("sorted original array before clonning");
        game_popularity.sort((a, b) => a.popularity < b.popularity);
        console.log(game_popularity);


        console.log("clone using object assign");
        const cl2 = game_popularity.map(a => Object.assign({}, a));
        cl2[1].game = "clash of titan";
        cl2.push({ game: "logan", popularity: 57 });
        console.log(cl2);


        //adding new array element doesnt reflect in original array
        console.log("clone using concat");
        var ph = []
        var cl = ph.concat(game_popularity);

        //copied by reference ?
        cl[0].game = "rise of civilization";

        game_popularity[0].game = 'ping me';
        cl.push({ game: "angry bird", popularity: 67 });
        console.log(cl);

        console.log("clone using ellipses");
        var cl3 = [...game_popularity];
        cl3.push({ game: "blue whale", popularity: 67 });
        cl3[2].game = "harry potter";
        console.log(cl3);

        console.log("clone using json.parse");
        var cl4 = JSON.parse(JSON.stringify(game_popularity));
        cl4.push({ game: "home alone", popularity: 87 });
        cl4[3].game ="lockhead martin";
        console.log(cl4);

        console.log("clone using Object.create");
        var cl5 = Array.from(Object.create(game_popularity));
        cl5.push({ game: "fish ville", popularity: 87 });
        cl5[3].game ="veto power";
        console.log(cl5);


        //array function
        console.log("sorted original array after clonning");
        game_popularity.sort((a, b) => a.popularity < b.popularity);
        console.log(game_popularity);


        console.log("Object.assign deep clone object array");
        console.log("json.parse deep clone object array");
        console.log("concat does not deep clone object array");
        console.log("ellipses does not deep clone object array");
        console.log("Object.create does not deep clone object array");


        Output:


        sorted original array before clonning
        [ { game: 'temple runner', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'ludo', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 } ]
        clone using object assign
        [ { game: 'temple runner', popularity: 86 },
        { game: 'clash of titan', popularity: 78 },
        { game: 'ludo', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'logan', popularity: 57 } ]
        clone using concat
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'ludo', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'angry bird', popularity: 67 } ]
        clone using ellipses
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'blue whale', popularity: 67 } ]
        clone using json.parse
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'lockhead martin', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'home alone', popularity: 87 } ]
        clone using Object.create
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'veto power', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'fish ville', popularity: 87 } ]
        sorted original array after clonning
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'veto power', popularity: 40 },
        { game: 'road runner', popularity: 20 } ]

        Object.assign deep clone object array
        json.parse deep clone object array
        concat does not deep clone object array
        ellipses does not deep clone object array
        Object.create does not deep clone object array

person1 = {
    name: 'Naved',
    last: 'Khan',
    clothes: {
        jens: 5,
        shirts: 10
    }
};

person2 = {
    name: 'Naved',
    last: 'Khan'
};

// first way  shallow copy single lavel copy 
// const person3 = { ...person1 };

// secound way shallow copy single lavel copy
// const person3 = Object.assign({}, person1);

// third  way shallow copy single lavel copy but old 
// const person3 = {};
// for (let key in person1) {
//  person3[key] = person1[key];
// }

// deep copy with array and object best way
const person3 = JSON.parse(JSON.stringify(person1));

    person3.clothes.jens = 20;

console.log(person1);
console.log(person2);
console.log(person3);

This deeply copies arrays, objects, null and other scalar values, and also deeply copies any properties on non-native functions (which is pretty uncommon but possible). (For efficiency, we do not attempt to copy non-numeric properties on arrays.)

function deepClone (item) {
  if (Array.isArray(item)) {
    var newArr = [];
    for (var i = item.length; i-- > 0;) {
      newArr[i] = deepClone(item[i]);
    }
    return newArr;
  }
  if (typeof item === 'function' && !(/\(\) \{ \[native/).test(item.toString())) {
    var obj;
    eval('obj = '+ item.toString());
    for (var k in item) {
      obj[k] = deepClone(item[k]);
    }
    return obj;
  }
  if (item && typeof item === 'object') {
    var obj = {};
    for (var k in item) {
      obj[k] = deepClone(item[k]);
    }
    return obj;
  }
  return item;
}

lodash has cloneDeep function for this purposes:

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);

This method is very simple and you can modify your clone without modify the original array.

_x000D_
_x000D_
// Original Array_x000D_
let array = [{name: 'Rafael'}, {name: 'Matheus'}];_x000D_
_x000D_
// Cloning Array_x000D_
let clone = array.map(a => {return {...a}})_x000D_
_x000D_
// Editing the cloned array_x000D_
clone[1].name = 'Carlos';_x000D_
_x000D_
_x000D_
console.log('array', array)_x000D_
// [{name: 'Rafael'}, {name: 'Matheus'}]_x000D_
_x000D_
console.log('clone', clone)_x000D_
// [{name: 'Rafael'}, {name: 'Carlos'}]
_x000D_
_x000D_
_x000D_


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

I'm answering this question because there doesn't seem to be a simple and explicit solution to the problem of "cloning an array of objects in Javascript":

function deepCopy (arr) {
    var out = [];
    for (var i = 0, len = arr.length; i < len; i++) {
        var item = arr[i];
        var obj = {};
        for (var k in item) {
            obj[k] = item[k];
        }
        out.push(obj);
    }
    return out;
}

// test case

var original = [
    {'a' : 1},
    {'b' : 2}
    ];

var copy = deepCopy(original);

// change value in copy
copy[0]['a'] = 'not 1';

// original[0]['a'] still equals 1

This solution iterates the array values, then iterates the object keys, saving the latter to a new object, and then pushing that new object to a new array.

See jsfiddle. Note: a simple .slice() or [].concat() isn't enough for the objects within the array.


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

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


If you want to implement deep clone use JSON.parse(JSON.stringify(your {} or []))

_x000D_
_x000D_
const myObj ={_x000D_
    a:1,_x000D_
    b:2,_x000D_
    b:3_x000D_
}_x000D_
_x000D_
const deepClone=JSON.parse(JSON.stringify(myObj));_x000D_
deepClone.a =12;_x000D_
console.log("deepClone-----"+myObj.a);_x000D_
const withOutDeepClone=myObj;_x000D_
withOutDeepClone.a =12;_x000D_
console.log("withOutDeepClone----"+myObj.a);
_x000D_
_x000D_
_x000D_


For cloning the objects as well I was just going to suggest ECMAScript 6 reduce():

const newArray=myArray.reduce((array, element)=>array.push(Object.assign({}, element)), []);

But frankly I like the answer of @dinodsaurus even better. I'm just putting this version here as another option, but personally I'll be using map() as suggested by @dinodsaurus .


My approach:

var temp = { arr : originalArray };
var obj = $.extend(true, {}, temp);
return obj.arr;

gives me a nice, clean, deep clone of the original array - with none of the objects referenced back to the original :-)


Some elegant ways for deep cloning in javascript

https://mootools.net/core/docs/1.6.0/Types/Object

https://scotch.io/bar-talk/copying-objects-in-javascript

1) A vanilla Javascript method for cloning objects

2) A clever exploit of the JSON library to deep-clone objects

3) Using jQuery’s $.extend() function

4) Using Mootools’ clone() function to clone objects