Deep copying Javascript Arrays

Ziyo Shams
4 min readJun 26, 2018

--

Most people know the concept of shallow copy, but they come across deep copy less often. Let’s start by defining what these two terms mean:

What is “shallow copy”?

Before we talk about shallow copy, let’s quickly talk about reference to an object. Let’s use a simple example by declaring a variable that holds values for a one dimensional array.

const array1 = [1, 2, 3, 4, 5, 6, 7, 8]

And then we try to “copy” it to another variable.

const array2 = array1;

Guess what? Arrays are mutable type in Javascript! That means the value of the array can be changed after it has been declared. In this case array2 is copying the reference of array1. This is not a shallow copy, rather it is called copying a reference of an object. In short, both variables are pointing to the same memory address. It would look like this:

Whenever you modify one of these arrays, you will be modifying both arrays. There are a few cases where you might want to do that, but in most cases you are advised to avoid mutating original values.

Shallow copy stands in between copy by reference and deep copy. According to MDN Array.prototype.slice() returns a shallow copy of the selected portion of the array. A quick example:

const array1 = [1, 2, 3, [4, 5], 6];
const array2 = array1.slice(); // array2 = [1, 2, 3, [4, 5], 6];

At first it looks like we deep copied an array, except we didn’t. This is an example of a shallow copy. Indeed we copied 1, 2, 3, 6 to array2, but we copied a reference of [4, 5] . If we were to change one of the values in array1[0] = 'a'; our array2 values will stay the same. If we change a value in array1[3][0] = 'x' our array2 will be modified. Another example:

const array1 = [[1, 'a'], 2, 3, 4, 5];
const array2 = array1.slice();
console.log(array1); // [[ 1, 'a' ], 2, 3, 4, 5]
console.log(array2); // [[ 1, 'a' ], 2, 3, 4, 5]
array1[0][1] = 'b'; // changing array1 values only
array1[2] = 100
console.log(array1); // [[ 1, 'b' ], 2, 100, 4, 5]
console.log(array2); // [[ 1, 'b' ], 2, 3, 4, 5]
// array2 has been modified

How do we go about this? Of course deep copying an array.

What is “deep copy”?

Short answer — it is the opposite of shallow copy. Deep copy means that variables don’t share the same memory address, and we don’t copy anything by reference. A variable would have a copy of the original array in a different memory location.

Let’s see some examples of how we deep copy an array. There are many ways to approach this depending how complex the array is. To start with, let’s use the same example (one dimensional array):

const array1 = [1, 2, 3, 4, 5, 6, 7, 8]

Approach #1

// Use built-in array methods
const array2 = array1.map(elem => elem);

Approach #2

// Use Spread operator
const array2 = [...array1];

Approach #3

const array2 = [];array1.forEach(elem => {
array2.push(elem)
});

All of above mentioned methods are fine if and only if your array is one dimensional or it does not contain any other mutable type(object). In case an array contains another array as one of its elements const array1 = [1, 2, 3, ['a', 'b', 'c', 'd'], 5, 6, 7, 8], none of the above mentioned methods would work. The issue is how Array structure is implemented in Javascript.

This is what happens when you try to deep copy using one of the above mentioned methods. This illustration may not be 100% accurate, but it will give you a rough idea how array structure is implemented.

In order to copy elements properly, we have to go and copy each element individually. One of many solutions to deep copying a multidimensional array is iterating through the array recursively. Here is a quick example of a multidimensional array and recursive solution:

// set up a function that iterates through a given array
// if one of the elements is an array, call itself with that element
// (Edited)
// if elements of the array is an object, we make sure to take care of that too.
const deepCopy = (arr) => {
let copy = [];
arr.forEach(elem => {
if(Array.isArray(elem)){
copy.push(deepCopy(elem))
}else{
if (typeof elem === 'object') {
copy.push(deepCopyObject(elem))
} else {
copy.push(elem)
}
}
})
return copy;
}
// Helper function to deal with Objects
const deepCopyObject = (obj) => {
let tempObj = {};
for (let [key, value] of Object.entries(obj)) {
if (Array.isArray(value)) {
tempObj[key] = deepCopy(value);
} else {
if (typeof value === 'object') {
tempObj[key] = deepCopyObject(value);
} else {
tempObj[key] = value
}
}
}
return tempObj;
}
const array1 = [
1,
2,
[6, 7, 8],
'foo',
['bar', [9, 10], 11],
12,
13
];
const array2 = deepCopy(array1);
// now array2 has a separate copy of array1

That was it! Hope this was informative. Here is an example that you can play with. Examine the code, look at the console and you should be able to form a deeper understanding of shallow and deep copy. 👇👇👇👇👇

--

--