606

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
wallyqs
  • 7,456
  • 5
  • 26
  • 25
  • [What is the most efficient way to deep clone an object in JavaScript?](https://stackoverflow.com/q/122102/104380) – vsync Oct 24 '20 at 12:32

33 Answers33

767

Creating a deep copy with structuredClone

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

array2 = structuredClone(array1);

However, this function is relatively new (Chrome 98, Firefox 94) and is currently only available to about 85% of users, so it's not ready for production yet 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.*

416E64726577
  • 2,214
  • 2
  • 23
  • 47
Vladimir Kharlampidi
  • 9,335
  • 2
  • 16
  • 13
  • 144
    This might work for JSON data, but if your array contains any functions or instances of objects that have methods, say goodbye to them. – sp0rkyd0rky Oct 23 '14 at 01:43
  • 14
    be careful if you have an array which contains the value Infinity. This value gets lost (is null afterwards). (http://jsfiddle.net/klickagent/ehm4bd3s/) – klickagent.ch Feb 01 '15 at 16:16
  • 15
    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. – XML Sep 07 '15 at 09:38
  • 1
    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?! – Herr_Hansen Oct 13 '15 at 00:07
  • 2
    if object of array has DateTime, then string will be returned instead of DateTime! new Date !== JSON.parse(JSON.stringify(new Date)) – MarkosyanArtur Jan 15 '16 at 07:41
  • @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. – Sparr Mar 13 '17 at 03:19
  • 5
    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?_ – XML Mar 14 '17 at 04:35
  • This only traded my "too much recursion" error for a "cyclic object value" error. – HoldOffHunger Aug 16 '17 at 15:48
  • 1
    it's ugly but is the only method in 2018 ?? – stackdave Mar 03 '18 at 15:20
  • 2
    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) – Agargara Jun 06 '18 at 08:31
  • Nop, if you have a circular structure this method will throw an error on stringify as well as jQuery.extend – gaugeinvariante Oct 10 '18 at 10:11
  • 1
    This takes a long time. If you have 50+ objects, don't do this. – Phil Mar 12 '20 at 22:45
  • Not the best way to go for but should work in most cases. – Steve Moretz Feb 03 '21 at 13:32
  • This should not be the best performant solution – srknzl Mar 25 '21 at 14:19
393

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}));
Soviut
  • 88,194
  • 49
  • 192
  • 260
dinodsaurus
  • 4,937
  • 4
  • 19
  • 24
  • 24
    But if myArray contained a bunch of Dinosaurs, newArray contains a bunch of Objects. That's lame, don't you agree? – Matthew James Davis Mar 27 '17 at 20:09
  • 4
    best approach, as it keeps objects functions alive, rathen then losing them with JSON.parse(JSON.stringify(nodesArray)) – scipper Sep 08 '17 at 13:30
  • 26
    @MatthewJamesDavis you can solve this by replacing `{}` with `new Dinosaur()`. – Agargara Jun 06 '18 at 08:32
  • 13
    shallow copy not deep copy – Sultan Aslam Feb 13 '19 at 19:11
  • ie 11 doesn't support Object.assign – Firemen26 Feb 19 '19 at 14:40
  • 8
    This works great for an array of objects, if those objects only contain primitive properties... which is what i needed, thanks – mojave Feb 19 '19 at 20:55
  • @Firemen26 you should transpile you production code using [Babel](https://babeljs.io/) – dinodsaurus Feb 20 '19 at 15:10
  • @dinodsaurus I doesn't use node. My app is an old app. – Firemen26 Feb 21 '19 at 09:02
  • 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' }))); – Joseph Feb 21 '19 at 13:04
  • 1
    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. – ADJenks Nov 26 '19 at 22:02
  • @ADJenks 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. – Dror Bar May 11 '20 at 11:43
  • @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. – ADJenks May 11 '20 at 21:02
  • worked for me in vuejs 2 and nuxt 2. ES6 spread syntax did not worked so i needed an alternative – Tanktiger Nov 13 '20 at 13:17
162

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

new_array = old_array.slice(0);
Leopd
  • 41,333
  • 31
  • 129
  • 167
  • 8
    I don't think you have to pass `0`, you can just call `.slice()` at least in chrome anyway – slf Apr 23 '13 at 17:51
  • 134
    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. – bozdoz Dec 30 '13 at 23:17
  • 45
    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. – S. A. Mar 07 '14 at 21:51
  • 4
    This will work only for "generics" int, string etc. but not for an array of objects. – Stefan Michev Nov 19 '14 at 17:09
  • 8
    for array of objects this doesn't actually clone, update to the new_array will also update the old_array . – Anas Mar 30 '16 at 15:28
  • @StefanMichev The term for int, string, etc. is "primitives", generics is something else :) – Charles Wood Aug 05 '21 at 22:46
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.
Medeni Baykal
  • 4,223
  • 1
  • 28
  • 36
Dan Lew
  • 85,990
  • 32
  • 182
  • 176
  • 1
    Fixed link for @PatrickdeKleijn answer: https://web.archive.org/web/20140222022056/http://my.opera.com/GreyWyvern/blog/show.dml/1725165 – Mike Szyndel Dec 08 '15 at 17:25
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} ]
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
MennyMez
  • 2,426
  • 18
  • 18
  • 2
    Good modern answer, that won't work with older browsers (like IE 11) – Jealie Feb 20 '17 at 20:00
  • 1
    @Jealie 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. – ruffin Apr 06 '17 at 21:21
  • 92
    This just deep copies the array, not each object in the array. – Toivo Säwén Jul 18 '17 at 08:52
  • 46
    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. – Joel Kinzel Nov 16 '17 at 23:39
  • 4
    It works for primitives only. Try this: objArray[0].a = 3; and you will see the object's reference remains the same in clonedArray. – Sergio Correa Jul 11 '18 at 21:49
  • 1
    It is still shallow clone, doesn't work on array of objects. – DicBrus Jul 23 '18 at 07:39
  • 1
    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. – What Would Be Cool Aug 13 '18 at 14:32
  • its a shallow copy of the array, not a deep one. The array is what is being cloned not the objects them self. – MennyMez Aug 27 '18 at 08:10
  • 1
    This still has the exact problem that the OP was trying to avoid. – Herohtar Oct 22 '19 at 20:45
  • 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 deepclone – bams Feb 11 '21 at 14:53
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);
                  });
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
viliks
  • 349
  • 3
  • 5
  • 2
    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. – bozdoz Dec 30 '13 at 23:24
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);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sudheer nunna
  • 1,659
  • 15
  • 17
17
$.evalJSON($.toJSON(origArray));
alex
  • 479,566
  • 201
  • 878
  • 984
elsereturn
  • 227
  • 2
  • 2
  • 2
    You will need to be using the jquery json plugin to use this http://code.google.com/p/jquery-json/ – wmitchell Aug 04 '11 at 15:00
  • 36
    Without JQ (fine in modern browsers): `JSON.parse(JSON.stringify(origArray));` – forresto May 27 '13 at 12:10
  • 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. – wavedrop Aug 23 '13 at 15:29
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;
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
eomeroff
  • 9,599
  • 30
  • 97
  • 138
  • The `Object.keys` thing here is more verbose but not more compatible than `Object.assign`, and has the `__proto__` bug. – Ry- Jun 04 '23 at 04:36
11

Lodash has the cloneDeep function for these purposes:

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
DicBrus
  • 1,291
  • 9
  • 9
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
bozdoz
  • 12,550
  • 7
  • 67
  • 96
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));
Yi Jiang
  • 49,435
  • 16
  • 136
  • 136
George
  • 99
  • 1
  • 1
  • 24
    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. – Nicolas R Jun 18 '10 at 14:07
  • 1
    And if they don't use [json2](https://github.com/douglascrockford/JSON-js), not `eval`. – kamranicus Jan 09 '12 at 18:02
  • This has terrible performance, but unfortunately is the best answer I've seen :/ – Dvid Silva Jun 25 '14 at 20:13
  • Don't eval anything with user data. Preferably never use `eval()` at all. It's a security risk. – ADJenks Nov 26 '19 at 22:06
  • Won't this fail if the array's object structure has circular references? Which I am under the impression that it does...? – Kjell Rilbe Apr 02 '21 at 06:05
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'}]
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Rafael Grilli
  • 1,959
  • 13
  • 25
  • 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. – Ben Wheeler Jul 17 '19 at 18:02
  • 1
    true deep clone can be done using `lodash.clonedeep` from npm – revelt Jan 27 '20 at 19:55
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.)

Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
5

from 2022, we can use structuredClone to deep copy.

structuredClone(array)
Vinoth
  • 972
  • 1
  • 16
  • 47
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 :-)

nebulae
  • 2,665
  • 1
  • 19
  • 16
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);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Stef
  • 2,603
  • 2
  • 18
  • 28
4

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];
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chtioui Malek
  • 11,197
  • 1
  • 72
  • 69
  • 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. – Ben Wheeler Jul 17 '19 at 17:53
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);
Brak
  • 178
  • 1
  • 9
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;
}
Brett Zamir
  • 14,034
  • 6
  • 54
  • 77
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Russell Munro
  • 479
  • 5
  • 9
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;
};
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
krupar
  • 349
  • 3
  • 5
  • 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). – Ry- Jun 04 '23 at 04:46
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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Franck Dernoncourt
  • 77,520
  • 72
  • 342
  • 501
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));
Redu
  • 25,060
  • 6
  • 56
  • 76
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;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sudhakar
  • 3,104
  • 2
  • 27
  • 36
1

With jQuery:

var target = [];
$.each(source, function() {target.push($.extend({}, this));});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lujan99
  • 29
  • 3
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)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Romuald
  • 545
  • 1
  • 7
  • 16
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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sangram
  • 377
  • 4
  • 5
0

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

Ulf Aslak
  • 7,876
  • 4
  • 34
  • 56
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
Vasco
  • 418
  • 1
  • 8
  • 12
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;
}
Bhuwan
  • 16,525
  • 5
  • 34
  • 57
-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;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ozantunca
  • 128
  • 1
  • 3
  • 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. – Brett Zamir Oct 03 '16 at 22:07
-2

I think we can do this ->

let a = [{name: "mike"}, {name: "ron"}];
let copy_a = JSON.parse(JSON.stringify(a));
Sadia
  • 91
  • 1
  • 4
  • 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`. – AlexandreS May 15 '23 at 13:01