DEV Community

How to Deep Clone an Array in JavaScript

Samantha Ming on March 04, 2019

There are 2 types of array cloning: shallow & deep. Shallow copies only cover the 1st level of the array and the rest are referenced. If you ...
Collapse
 
tailcall profile image
Maria Zaitseva

One has to be really careful with JSON solution! It doesn't work with values not compatible with JSON. Examples:

function nestedCopy(array) {
    return JSON.parse(JSON.stringify(array));
}

// undefineds are converted to nulls

nestedCopy([1, undefined, 2]) // -> [1, null, 2]

// DOM nodes are converted to empty objects

nestedCopy([document.body, document.querySelector('p')]) // -> [{}, {}]

// JS dates are converted to strings

nestedCopy([new Date()]) // -> ["2019-03-04T10:09:00.419Z"]
Enter fullscreen mode Exit fullscreen mode

Consider using a library function if you have to work with such data.

Collapse
 
samanthaming profile image
Samantha Ming

Yes! Very good point, let me add that to the code notes! Thanks for noting that 👏

Collapse
 
vipinkumarsn4 profile image
Vipin Saini

Wow.
This is really cool!
You have added a gem to the post!
Thanks!

Collapse
 
veevidify profile image
V • Edited

Nice summary!

I'd like to add a tiny bit to the nested array example, that when you rest-spread an array, if that array has another nested array as its element, what gets shallow-copied is the reference to the original nested array:

let nestedArray = [1, [2], 3];
let arrayCopy = [...nestedArray]; // arrayCopy == [1, ref, 3];

thus creating the mutation effects.

Collapse
 
samanthaming profile image
Samantha Ming

Yup! In those cases, if you want a true copy, you will need to do a deep clone. Thanks for sharing!

Collapse
 
guico33 profile image
guico33

As pointed out in other comments, relying on JSON.parse and JSON.stringify to perform a deep cloning is suboptimal (edge cases, performances). Better use the method provided by some library (e.g lodash's cloneDeep).

Collapse
 
worc profile image
worc

pulling in an entire library for a single method can also be a kind of suboptimal (versioning, security auditing, vendoring vs. managed dependencies). it's a lot of overhead when there might be a better solution in just not doing deeply nested arrays.

Collapse
 
guico33 profile image
guico33 • Edited

My point is to use an optimised method. Deep cloning arrays or objects may not be very common but if the need arises, I'd recommend using an exisiting solution.

Collapse
 
samanthaming profile image
Samantha Ming

Totally! JSON is the quick&dirty way 😂Lodash is definitely the preferred optimal solution. Thanks for pointing that out 👍

Collapse
 
aloksdiptim profile image
alokz diptim! • Edited

This right here deep copies nested array too

    let cloneMatrix  = [];

    for(let m = 0; m < matrix.length; m++){
        cloneMatrix.push([...matrix[m]]);
    }
Enter fullscreen mode Exit fullscreen mode

It pushes into a new array with [...matrix[m]]

Collapse
 
alfredosalzillo profile image
Alfredo Salzillo • Edited

Hi,
nice post, but i'd like you to note that there are some difference between deepClone and JSON.stringify/parse.

  • JSON.stringify/parse only work with Number and String and Object litteral without function or Symbol properties.
  • deepClone work with all types, function and Symbol are copied by reference.

Here an example:

const lodashClonedeep = require("lodash.clonedeep");

const arrOfFunction = [() => 2, {
    test: () => 3,
}, Symbol('4')];

// deepClone copy by refence function and Symbol
console.log(lodashClonedeep(arrOfFunction));
// JSON replace function with null and function in object with undefined
console.log(JSON.parse(JSON.stringify(arrOfFunction)));

// function and symbol are copied by reference in deepClone
console.log(lodashClonedeep(arrOfFunction)[0] === lodashClonedeep(arrOfFunction)[0]);
console.log(lodashClonedeep(arrOfFunction)[2] === lodashClonedeep(arrOfFunction)[2]);
Collapse
 
samanthaming profile image
Samantha Ming

Exactly! Thanks for sharing this! Let me add it to my code notes 👍

Collapse
 
rishisetpal profile image
Rishi Setpal • Edited

There is no point in compairing: new Object with new Object
lodashClonedeep(arrOfFunction)[0] === lodashClonedeep(arrOfFunction)[0]

Instead Compare: old Object with new Object
arrOfFunction[0] === lodashClonedeep(arrOfFunction)[0]


// Just a code snippet
// Change Direactory: cd "current_working_directory"
// npm install lodash or npm install lodash.clonedeep
const lodashClonedeep = require("lodash.clonedeep");
const arrOfFunction = [
    () => 2, 
    { test: () => 3,}, 
    Symbol('4')
];
// deepClone copy by refence function and Symbol
console.log(lodashClonedeep(arrOfFunction));
// JSON replace function with null and function in object with undefined
console.log(JSON.parse(JSON.stringify(arrOfFunction)));
// function and symbol are copied by reference in deepClone
console.log(arrOfFunction[0] === lodashClonedeep(arrOfFunction)[0]);
console.log(arrOfFunction[2] === lodashClonedeep(arrOfFunction)[2]);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
alinalapina profile image
Alina Lapina

In the last clone (recursive code) the function have to return a copy of item, {...item}, in order to create deep cloning, not the item reference.
The whole piece of code is:

const clone = (items) => items.map(item => Array.isArray(item) ? clone(item) : {...item});

Collapse
 
ocdalex profile image
Alex Walker

Nicely explained. Another quirk of JavaScript I'l know to look out for, but your JSON solution is simple to remember.

Collapse
 
shayd16 profile image
Shayne Darren • Edited

I don't think this is javascript specific? And no, it's not a quirk.
Even Java(and most other languages) copies non-primitive data types by reference.
Maybe I misunderstood your comment though.

Collapse
 
ocdalex profile image
Alex Walker

I'm a primarily a PHP developer so everything about JavaScript is quirky to me. :-)

Thread Thread
 
shayd16 profile image
Shayne Darren

Let's just pretend I left the obligatory, "But php has a,b,c,d....x,y,z quirks and javascript is so much better" ;)

Collapse
 
samanthaming profile image
Samantha Ming

JSON is your quick and dirty solution. For a more robust solution, I’d go with Lodash 🙂

Collapse
 
vidhyakrish profile image
vidhyakrish

Array.form() will do the same operations and same behaviour as [...] spread.

for eg.

let nestedArray = [1, [2], 3];
let arrayCopy = Array.from(nestedArray);

// Make some changes
arrayCopy[0] = '👻'; // change shallow element
console.log(nestedArray); //[1,[2],3]

console.log(arrayCopy); // [ '👻',[2],3]
arrayCopy[1][0] = '💩'; // change nested element
console.log(nestedArray); // [ 1, [ '💩' ], 3 ]

Collapse
 
pradyumnagupta profile image
PradyumnaGupta • Edited

structuredClone API is the more cleaner way to do this now and it is accepted by almost all the browsers now :

array2 = structuredClone(array1);

Collapse
 
cuterthanbacon profile image
CuterThanBacon

how would you code the recursion example without using map?

Collapse
 
vitalcog profile image
Chad Windham • Edited

If you want/need to not use ES6 stuff you would simply use a standard for loop.


// Old javascript

function cloneDeeply(arrayToClone) {

  var newArray = [];

  for (var i = 0; i < arrayToClone.length; i++) {

    if (arrayToClone[i].isArray) {
      cloneDeeply(arrayToClone[i])
    }
    else {
      newArray.push(arrayToClone[i])
    }
  }

  return newArray;
}

// Then to use it

var clone = cloneDeeply(yourArrayToCloneGoesHere);

I didn't test this or anything btw just writing an example off of the top of my head, so no promises lol

Collapse
 
bossajie profile image
Boss A

I love it! In my current project, I encountered this kind of problem especially in passing by reference of nested arrays. Now, I got it by very friendly explanation. Good job!

Collapse
 
samanthaming profile image
Samantha Ming

It took me a long time to get this reference type. So hopefully I was able to explain it a way that makes sense to you 😄

Collapse
 
surajsharma profile image
Suraj Sharma

very helpful, thank you!

Collapse
 
samanthaming profile image
Samantha Ming

You’re welcome! Thanks for reading it 😃

Collapse
 
vipinkumarsn4 profile image
Vipin Saini

Hi Samantha,

Good Post!
Thanks!

Collapse
 
caseycole589 profile image
Casey Cole

this is the best way to deep copy and you should be cloning html element refs as other people said in the comments that is and easy way to get memory leaks

Collapse
 
giorgos profile image
GeorgeNeilyo

Amazing post. Simple explanation,simple examples.

Thank you!