How do you clone an array of objects in JavaScript?
Asked Answered
O

33

618

...where each object also has references to other objects within the same array?

When I first came up with this problem I just thought of something like

var clonedNodesArray = nodesArray.clone()

would exist and searched for information on how to clone objects in JavaScript. I did find a question on Stack Overflow (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, and 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?

Orff answered 28/2, 2009 at 5:56 Comment(1)
N
787

Creating a deep copy with structuredClone

The modern way to deep copy an array in JavaScript is to use structuredClone:

array2 = structuredClone(array1);

This function is relatively new (Chrome 98, Firefox 94) and is currently available to about 95% of users, so may not ready for production without a polyfill.

As an alternative, you can use one of the well-supported JSON-based solutions below.

Creating a deep copy with JSON.parse

A general solution, that accounts for all possible objects inside an Array of objects may not be possible. That said, if your array contains objects that have JSON-serializable content (no functions, no Number.POSITIVE_INFINITY, etc.) one simple way to avoid loops, at a performance cost, is this pure vanilla one-line solution.

let 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 is ~30 times slower than the spread method.

If you have shallow objects in the array, and IE6 is acceptable, a better approach is to use the spread operator combined with the .map array operator. For a two levels deep situation (like the array in the Appendix below):

clonedArray = nodesArray.map(a => {return {...a}})

The reasons are two fold: 1) It is much, much faster (see below for a benchmark comparison) and it will also allow any valid object in your array.

*Appendix: The performance quantification is based on cloning this array of objects a million times:

 [{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic1.jpg?raw=true', id: '1', isFavorite: false}, {url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic2.jpg?raw=true', id: '2', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic3.jpg?raw=true', id: '3', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic4.jpg?raw=true', id: '4', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic5.jpg?raw=true', id: '5', isFavorite: true},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic6.jpg?raw=true', id: '6', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic7.jpg?raw=true', id: '7', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic8.jpg?raw=true', id: '8', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic9.jpg?raw=true', id: '9', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic10.jpg?raw=true', id: '10', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic11.jpg?raw=true', id: '11', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic12.jpg?raw=true', id: '12', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic13.jpg?raw=true', id: '13', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic14.jpg?raw=true', id: '14', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic15.jpg?raw=true', id: '15', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic16.jpg?raw=true', id: '16', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic17.jpg?raw=true', id: '17', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic18.jpg?raw=true', id: '18', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic19.jpg?raw=true', id: '19', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic20.jpg?raw=true', id: '20', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic21.jpg?raw=true', id: '21', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic22.jpg?raw=true', id: '22', isFavorite: false},{url: 'https://github.com/bobziroll/scrimba-react-bootcamp-images/blob/master/pic23.jpg?raw=true', id: '23', isFavorite: false}]

either using:

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

or:

clonedArray = nodesArray.map(a => {return {...a}})

The map/spread approach took 0.000466 ms per pass and the JSON.parse and JSON.stringify 0.014771 ms per pass.*

Nica answered 5/5, 2014 at 20:19 Comment(14)
This might work for JSON data, but if your array contains any functions or instances of objects that have methods, say goodbye to them.Supernormal
be careful if you have an array which contains the value Infinity. This value gets lost (is null afterwards). (jsfiddle.net/klickagent/ehm4bd3s)Hower
This is just generally a bad approach unless your array contains only primitives, and/or objects which themselves contain only string/number/boolean primitives (even null and undefined will be problems, since JSON doesn't support them). Further, it's a vastly less efficient operation than old_array.slice(0);, which should work both better and faster.Bakeman
I had to clone an array with objects out of an "master" object to new_array. That I could manipulate new_array and refresh it from master as often as I want. JSON.parse(JSON.stringify(old_array)) did the job, both independent. Don't understand upvote for old_array.slice(0) method, that didn't work?!Nonfiction
if object of array has DateTime, then string will be returned instead of DateTime! new Date !== JSON.parse(JSON.stringify(new Date))Prayer
@XMLilley old_array.slice(0) doesn't handle the case where my array contains primitives and objects/arrays containing primitives and objects/arrays containing ... Which is a very common use case.Novokuznetsk
The key line in the OP's question, which this answer above ignores entirely: ...where each object also has references to other objects within the same array?Bakeman
This only traded my "too much recursion" error for a "cyclic object value" error.Jenninejennings
it's ugly but is the only method in 2018 ??Luis
While this is a good solution for lazy coders, it's pretty inefficient. Just because it's the 21st century doesn't mean speed doesn't matter. In my testing, it's about 4 times faster to loop over the array and use Object.assign() to clone each object. (See dinodsaurus' answer for an example)Lobby
Nop, if you have a circular structure this method will throw an error on stringify as well as jQuery.extendCherimoya
This takes a long time. If you have 50+ objects, don't do this.Saarinen
Not the best way to go for but should work in most cases.Piano
This should not be the best performant solutionRoderickroderigo
U
400

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}));
Unlovely answered 27/10, 2016 at 11:16 Comment(13)
But if myArray contained a bunch of Dinosaurs, newArray contains a bunch of Objects. That's lame, don't you agree?Timorous
best approach, as it keeps objects functions alive, rathen then losing them with JSON.parse(JSON.stringify(nodesArray))Minnesinger
@MatthewJamesDavis you can solve this by replacing {} with new Dinosaur().Lobby
shallow copy not deep copySynovitis
ie 11 doesn't support Object.assignDelano
This works great for an array of objects, if those objects only contain primitive properties... which is what i needed, thanksNightlong
@Delano you should transpile you production code using BabelUnlovely
@Unlovely I doesn't use node. My app is an old app.Delano
this works perfectly for objects and you can even change the object you want by adding or changing properties with const newArray = myArray.map(a => (Object.assign({}, a, { some-prop: 'value' })));Babbittry
This is slightly deeper, it creates new objects for each element, but if the objects contain objects themselves, they will not be copied, they will be references.Slating
@Slating Basically this goes one level deep (Array of Objects), which is the most common use-case. If you have two levels deep (Array of Objects of Objects) or more, you need to go deeper with the method iteration as well.Suppose
@DrorBar Not sure why you told me that. What I was trying to add was that it actually goes 2 levels deep, because the arrays are different arrays and the objects are new objects as well. 1 level would be a new array with the elements all being references. This is an example of what I was trying to point out: a = [{z:1}];b = [a[0]];console.log(a[0] == b[0]); a = [{z:1}];c = [Object.assign({}, a[0])];console.log(a[0] == c[0]) The first equality is true, whereas the second one is false. Yes, I know they are shallow copied objects, that's what I said.Slating
worked for me in vuejs 2 and nuxt 2. ES6 spread syntax did not worked so i needed an alternativeAmorete
D
162

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

new_array = old_array.slice(0);
Depew answered 9/11, 2010 at 18:5 Comment(6)
I don't think you have to pass 0, you can just call .slice() at least in chrome anywayGrecism
This doesn't actually work though, does it? I mean, it's not an answer to the question how to clone an array of objects. This is the solution to clone a simple array.Burgonet
Actually this won't work for an objects array. The returned array by slice will be a new array but will contain the references to the original array objects.Andie
This will work only for "generics" int, string etc. but not for an array of objects.Billionaire
for array of objects this doesn't actually clone, update to the new_array will also update the old_array .Humblebee
@StefanMichev The term for int, string, etc. is "primitives", generics is something else :)Allerie
E
122

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.
Egest answered 28/2, 2009 at 8:4 Comment(1)
Fixed link for @PatrickdeKleijn answer: web.archive.org/web/20140222022056/http://my.opera.com/…Yamada
C
76

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 simplest 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} ]
Corneous answered 3/1, 2017 at 9:3 Comment(10)
Good modern answer, that won't work with older browsers (like IE 11)Fullback
@Fullback I'm going to guess KingpinEX is targeting this answer for folks transpiling es6 to something more universally useful with Babel or what have you.Antony
This just deep copies the array, not each object in the array.Primrose
To follow up on what @ToivoSäwén said, this will not deep copy the objects in the array. It will still reference the original objects so if you mutate them, it will impact the original array as well.Aftershaft
It works for primitives only. Try this: objArray[0].a = 3; and you will see the object's reference remains the same in clonedArray.Underdone
It is still shallow clone, doesn't work on array of objects.Elementary
try clonedArray[1].b = 200; then look at the original array. Like Sergio said, the object reference remains the same. The reason objArray[0] = {c:3}; seems to work is that you are replacing the pointer to the same object with a pointer to a new object in a different array. If you mutate the object in place you'll see that it's a reference.Brisk
its a shallow copy of the array, not a deep one. The array is what is being cloned not the objects them self.Corneous
This still has the exact problem that the OP was trying to avoid.Bolger
It doesn't solve the problem: clonedArray[0].a = 2323; console.log(objArray) it will log [ {a:2323} , {b:2} ]; You need to make a deepcloneSelfassertion
U
28

This works for me:

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

And if you need a deep copy of objects in the array:

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend(true, {}, obj);
                  });
Unfasten answered 22/6, 2012 at 18:41 Comment(1)
This looks like it would work. I'm trying to avoid extensive jQuery use, so I won't use it in my situation, but a for loop and for...in would work.Burgonet
L
18

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

const myObj ={
    a: 1,
    b: 2,
    b: 3
}

const deepClone = JSON.parse(JSON.stringify(myObj));
deepClone.a = 12;
console.log("deepClone-----"+myObj.a);
const withOutDeepClone = myObj;
withOutDeepClone.a = 12;
console.log("withOutDeepClone----" + myObj.a);
Lennox answered 4/3, 2020 at 10:56 Comment(0)
N
17
$.evalJSON($.toJSON(origArray));
Nildanile answered 4/8, 2010 at 20:0 Comment(3)
You will need to be using the jquery json plugin to use this code.google.com/p/jquery-jsonEpigraphy
Without JQ (fine in modern browsers): JSON.parse(JSON.stringify(origArray));Bowen
I found this comment useful. In my implementation I needed to make a copy of an array of objects that had KnockoutJS observable properties applied. The copy only needed the values, not the observable-ness. To make a copy of JUST the values I used JSON.parse(ko.toJSON(origArray)) OR ko.utils.parseJson(ko.toJSON(origArray)). Just my 2 cents and thank you for helping me arrive at my solution.Hesperides
R
13

from 2022, we can use structuredClone to deep copy.

structuredClone(array)
Radiosonde answered 22/6, 2022 at 19:35 Comment(1)
That is amazing! I guess I can mark this question as answered nowOrff
E
12

Lodash has the cloneDeep function for these purposes:

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
Elementary answered 23/7, 2018 at 7:43 Comment(0)
I
11

Map will create a new array from the old one (without reference to old one) and inside the map you create a new object and iterate over properties (keys) and assign values from the old Array object to corresponding 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;
});
Inquire answered 9/4, 2018 at 13:15 Comment(1)
The Object.keys thing here is more verbose but not more compatible than Object.assign, and has the __proto__ bug.Bayreuth
W
9

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));
Wild answered 31/5, 2010 at 8:23 Comment(5)
Some modern browsers have the JSON method built-in so you can do this: JSON.parse(JSON.stringify(MY_ARRAY)) which should be faster. Good suggestion.Ablative
And if they don't use json2, not eval.Zena
This has terrible performance, but unfortunately is the best answer I've seen :/Overtake
Don't eval anything with user data. Preferably never use eval() at all. It's a security risk.Slating
Won't this fail if the array's object structure has circular references? Which I am under the impression that it does...?Safko
B
9

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, iterates the object keys, saving the latter to a new object, and pushes that new object to a new array.

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

Burgonet answered 30/12, 2013 at 23:59 Comment(3)
thanks for the answer, but you should've highlighted the shortcomings of the answer. It doesn't work when the objects have objects in it.. right?Shatterproof
it will create shallow copy. not deepSynovitis
you need to add recursion somewhereClausius
D
9

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

// Original Array
let array = [{name: 'Rafael'}, {name: 'Matheus'}];

// Cloning Array
let clone = array.map(a => {return {...a}})

// Editing the cloned array
clone[1].name = 'Carlos';


console.log('array', array)
// [{name: 'Rafael'}, {name: 'Matheus'}]

console.log('clone', clone)
// [{name: 'Rafael'}, {name: 'Carlos'}]
Derr answered 4/5, 2017 at 3:55 Comment(2)
This does a shallow copy that's two levels deep, whereas [...oldArray] and oldArray.slice(0) do a shallow copy one level deep. So this is super useful, but not an actual full deep clone.Plier
true deep clone can be done using lodash.clonedeep from npmBrightwork
C
5

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

Cogswell answered 28/2, 2009 at 8:53 Comment(0)
P
5

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 new object.

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

let newObject = [...oldObject];
Puzzle answered 23/8, 2017 at 11:8 Comment(1)
Note that these approaches will only copy references for arrays and objects within the array, and won't make new copies of these. Expect this to break for multidimensional structures.Plier
L
4

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

Lathan answered 28/10, 2012 at 17:20 Comment(3)
This is the best solution using jquery. short and sweet.Chronograph
I did a performance test and this solution seems to be roughly 2x faster than the JSON.stringify solution.Destruct
Why is it working better than the other 15 previous answers?Swedish
V
4

jQuery extend is working fine. You just 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);
Viniculture answered 20/12, 2012 at 8:33 Comment(1)
After changing an object in first array, the object in second array gets modified, so it's not ok.Burnisher
H
3

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);
Haro answered 4/6, 2012 at 17:46 Comment(2)
deepclone? I use jquery-1.9.1 and it doesnot support this method. Is it method of more modern version?Elan
@user2783091 he is extending JQuery to add that function. Its not something that comes out of the boxPolio
S
3

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;
}
Swinford answered 3/10, 2016 at 22:38 Comment(0)
O
2

Forget eval() (it is the most misused feature of JavaScript 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;
};
Ostrogoth answered 10/1, 2012 at 11:21 Comment(1)
Extending native prototypes is bad practice, extending them with enumerable properties doubly so as you can see from the if (i != 'clone') check (imagine if you wanted to make a second function like this).Bayreuth
D
2

I am using Vue.js, so arrays/objects have other code tacked-on for Vue.js functionality. I tried many of the answers given, but I ended up using clone-deep.

Deach answered 4/6, 2021 at 7:20 Comment(0)
C
1

With jQuery:

var target = [];
$.each(source, function() {target.push($.extend({}, this));});
Centillion answered 2/4, 2012 at 23:29 Comment(0)
M
1

The following code will perform a deep copy of objects and arrays recursively:

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

Manipular answered 24/3, 2013 at 20:46 Comment(1)
arguments.callee is not available in strict mode and has performance problems otherwise.Swinford
J
1

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.

Array.prototype.clone = function(){
  return this.map(e => Array.isArray(e) ? e.clone() : e);
};

var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
    brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));
Jannelle answered 16/6, 2016 at 12:55 Comment(0)
P
1

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;
}
Proline answered 13/9, 2019 at 10:54 Comment(0)
P
1

Most concise solution for shallow copy:

array = array.map(obj => {
    return { ...obj };
});

This would make a completely independent copy of something like [{a: 1}, {b: 2}], but not [{a: {b: 2}}, {b: {a: 1}}].

Psychodiagnostics answered 7/10, 2021 at 20:49 Comment(0)
M
0

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

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

It looks like Babel is the fastest.

var x = babel({}, obj)
Maryalice answered 10/2, 2017 at 10:18 Comment(0)
B
0
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
Brevier answered 18/3, 2018 at 7:45 Comment(0)
P
0

In my case I have an array with many nested objects, so the other answers were not applicable to my use case.

I'm now using the npm package deep-copy-all:

npm i deep-copy-all
const deepCopy = require("deep-copy-all")

const copy = deepCopy(original)
copy[0].users[0].payments[0].product.price = 10

console.log(copy[0].users[0].payments[0].product.price) // 10
console.log(original[0].users[0].payments[0].product.price) // 5
Paley answered 16/3, 2022 at 8:2 Comment(0)
L
0

How about using a simple recursive function to get the deep copy of Object/Array

const deepCopyFunction = (inObject) => {
  const deepCopyObject = Array.isArray(inObject) ? [] : {};
  for (const key in inObject) {
    if (typeof inObject[key] === 'object') {
      deepCopyFunction(inObject[key]);
    }
    deepCopyObject[key] = inObject[key];
  }
  return deepCopyObject;
}
Lexicologist answered 12/7, 2022 at 13:23 Comment(0)
F
-1

I think I 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;
}
Faradmeter answered 4/6, 2015 at 14:24 Comment(1)
Object.create will treat item as the object's prototype, but that is different from cloning. If item is modified, changes will be reflected in its "clone" and vice versa. This approach does not work.Swinford
R
-2

I think we can do this ->

let a = [{name: "mike"}, {name: "ron"}];
let copy_a = JSON.parse(JSON.stringify(a));
Rockoon answered 2/2, 2023 at 11:22 Comment(1)
With your suggestion, copy_a will contain the same objects as a. If you change copy_a[0].name, it will also change a[0].name.Bendite

© 2022 - 2024 — McMap. All rights reserved.