Hello,

nowadays, most of development work is in JavaScript, some server-side, some client-side. Over the past  several months, I learned a lot about how not to do thing in JavaScript and a couple of things finally sunk in. One of them is how copy Arrays in JavaScript not using the "=" way, which, of course, is wrong.

With this post, I want to write down what I have learned about cloning arrays in JavaScript - especially and most  importantly because copying an array using = only copies a reference to the original array. Let's start with a sample to define to problem this creates:


const users = ['Peter','Paul','Mary'];
const copyOfUsers = users;

if(users === copyOfUsers) {
     console.log('Both are the same');
}

So, now that we have our reference copies, what will happen if we make changes to both objects ? Well - it will  simply change the data of BOTH objects since they both reference the same data in memory !
const users = ['Peter','Paul','Mary'];
const copyOfUsers = users;

users.push('Otto');
copyOfUsers.push('Hans');

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Paul','Mary','Otto','Hans'] in both cases !

Imagine deleting something or changing a value....

How can we work around this so we can have a real copy of an array in  case we need it  ? Well, there's the option to use the spread operator when creating the array like so:
const users = ['Peter','Paul','Mary'];
const copyOfUsers = [...users];

users.push('Otto');
copyOfUsers.push('Hans');

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Paul','Mary','Otto'] and ['Peter','Paul','Mary','Hans'] in these two cases !

Job done !

We could also concat an empty array and our existing array to get a new array like so:
const users = ['Peter','Paul','Mary'];
const copyOfUsers = [].concat(users);

users.push('Otto');
copyOfUsers.push('Hans');

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Paul','Mary','Otto'] and ['Peter','Paul','Mary','Hans'] in these two cases as well  !

Also, the .from derivate is able to do this like so:
const users = ['Peter','Paul','Mary'];
const copyOfUsers = Array.from(users);

users.push('Otto');
copyOfUsers.push('Hans');

console.log(users);
console.log(copyOfUsers);


again, this will log ['Peter','Paul','Mary','Otto'] and ['Peter','Paul','Mary','Hans'] in these two cases.

So problem solved ? Not so fast young padavan. This only works for the so called "Shallow copying" of arrays. This means, we have no nested object  structure inside of the array and all objects are on one, the so called first level.

What does that mean ? Look at this sample:
const users = ['Peter',['Paul'],'Mary'];
const copyOfUsers = [...users];

// modify the nested part in the copy:
copyOfUsers[1].[0] = 'Carl';

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Carl','Mary'] in these two cases, so the original users object got affected as well !

Now, to resolve this problem, there are two options to use "deep copying" instead of shallow copying:

Option 1: We can use the Lodash Library and the _.cloneDeep feature:
const users = ['Peter',['Paul'],'Mary'];
const copyOfUsers = _.cloneDeep(users);

// modify the nested part in the copy:
copyOfUsers[1].[0] = 'Carl';

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Carl','Mary'] and ['Peter',['Paul'],'Mary'] in these two cases, so the original users object has not been affected. Phew !

This is my preferred method as it is working with all sorts of data types and nesting structures, as well as functions and symbols. Keep in mind that functions and Symbols are copied by reference when using _.cloneDeep .

In case you can't use lodash for whatever reason, you can use option 2, bare in mind that if you need function and symbol support, this won't work..

Option 2: JSON.parse()
const users = ['Peter',['Paul'],'Mary'];
const copyOfUsers = JSON.parse(JSON.stringify(users));

// modify the nested part in the copy:
copyOfUsers[1].[0] = 'Carl';

console.log(users);
console.log(copyOfUsers);


So this will log ['Peter','Carl','Mary'] and ['Peter',['Paul'],'Mary'] in these two cases as well.

So, sometimes things are not that easy in JavaScript. You have to be aware of what you need to achieve, to pick the right way to solve a given problem. Hope this helps a bit in understanding the way to work with arrays and cloning.

Heiko.
Heiko Voigt   |   5 October 2020 13:53:20   |    AppDevPack    |  
  |   Next Document   |   Previous Document

Discussion for this entry is now closed.

Comments (0)

No Comments Found