In one of my previous articles I covered how you can copy objects in JavaScript. Copying an object is a pretty complicated endeavor, given that you would also have to be able to copy every other data type that could be in the object. But what if you're just copying an array? Like the last article, there are quite a few ways to perform this task, a few of which I'll go over in this article.
But first, a note on speed. While this might not matter for all applications, it's something to consider if copying large arrays is a common operation in your code, or if speed really matters. For some of the methods below I note its speed relative to the other methods, which comes from the results of this benchmark.
Copying Simple Arrays
For this first part, let's assume that the array you want to copy contains only primitive (and immutable) data types. That is, the array only contains numbers, booleans, strings, etc. This way we can focus more on the transfer of data from one array to another, as opposed to how we handle copying the actual contents of the array, which I'll cover in the section "Deep Copies" below.
There are a surprising number of ways to copy an array, some of which include:
push
- Spread
slice
Array.from
_.clone
The spread operator and the slice
method are the fastest ways to copy a shallow array, but keep in mind that this does depend on the underlying runtime, so that might not be universally true.
Push
This is probably the most obvious solution, which loops over the original array and uses the new array's push()
method to add elements from one array to another:
let oldArr = [3, 1, 5, 2, 9];
let newArr = [];
for (let i=0; i < oldArr.length; i++) {
newArr.push(oldArr[i]);
}
We simply loop over the array to be copied and push each element to the new array.
Spread
This method uses the spread operator, which was defined in ES6 and is available in most up-to-date browsers.
It works like the following:
let oldArr = [3, 1, 5, 2, 9];
let newArr = [...oldArr];
If I'm going to use a native solution and no third-party library then this is typically the solution I prefer thanks to its clean and simple syntax.
One important note is that this copying only works at the top level (like many of these methods), so it should not be used if you need deep copies of anything.
Slice
The slice()
method is typically used for returning a portion of an array, specified by the beginning
and end
parameters. If no parameters are passed, however, then a copy of the entire array is returned:
let oldArr = [3, 1, 5, 2, 9];
let newArr = oldArr.slice();
In many JavaScript runtimes this is the fastest way to copy an array.
Array.from
The Array.from
method is meant to create a shallow copy of any iterable you pass to it, and it also takes an optional mapping function as the second parameter. So it can be used to create an array out of strings, sets, maps, and of course, other arrays:
let oldArr = [3, 1, 5, 2, 9];
let newArr = Array.from(oldArr);
Lodash Clone
Lodash's clone() and cloneDeep() methods may be familiar to you if you read this article on copying objects. The methods do exactly what you'd expect - any object (or array, primitive, etc.) passed to it will be copied and returned.
_.cloneDeep
(described further below) is different in that it doesn't stop cloning at the top level, it will recursively copy all objects it encounters at any level.
Given this, we can use it to copy arrays as well:
let oldArr = [3, 1, 5, 2, 9];
let newArr = _.clone(oldArr);
_.clone
performs very well compared to the other methods, so if you're already using this library in your application then this is a simple solution.
Deep Copies
One important thing to point out is that all of the methods described above only perform shallow copies of your arrays. So if you have an array of objects, for example, the actual array will be copied, but the underlying objects will be passed by reference to the new array.
To demonstrate this problem, let's look at an example:
let oldArr = [{foo: 'bar'}, {baz: 'qux'}];
let newArr = [...oldArr];
console.log(newArr === oldArr);
console.log(newArr[0] === oldArr[0]);
false
true
Here you can see that while the actual array is new, the objects within it were not. For some applications this can be a big problem. If this applies to you then here are some other methods to try.
Lodash Clone Deep
Lodash's _.cloneDeep
method does exactly the same thing as _.clone()
, except that it recursively clones everything in the array (or object) you pass it. Using the same example as above, we can see that using _.cloneDeep()
will provide us with both a new array and copied array elements:
const _ = require('lodash');
let oldArr = [{foo: 'bar'}, {baz: 'qux'}];
let newArr = _.cloneDeep(oldArr);
console.log(newArr === oldArr);
console.log(newArr[0] === oldArr[0]);
false
false
JSON Methods
JavaScript does provide some handy JSON methods that handle converting most JS data types to a string, and then a valid JSON string to a JS object. The respective methods are used as follows:
let oldArr = [{foo: 'bar'}, {baz: 'qux'}];
let arrStr = JSON.stringify(oldArr);
console.log(arrStr);
let newArr = JSON.parse(arrStr);
console.log(newArr);
console.log(newArr === oldArr);
console.log(newArr[0] === oldArr[0]);
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
'[{"foo":"bar"},{"baz":"qux"}]'
[ { foo: 'bar' }, { baz: 'qux' } ]
false
false
This method works great, and it doesn't require any third-party libraries. However, there are two main issues:
- The data must be serializable and deserializable via JSON
- Using the JSON methods in this way is much slower than other solutions
So if you have data that can't be serialized to JSON or if speed is important for your application, then this might not be a good solution for you.
Conclusion
In this article I covered a number of ways that you can copy arrays in JavaScript, both using native code as well as a useful third-party library in Lodash. We also looked at the issue of deep cloning arrays and what solutions exist to solve it.
Is there a different method that works best for you? Let us know what you think in the comments.