This isn’t the first article about the spread operator and it won’t be the last. Nevertheless, if you’re someone who’s confused about those cryptic ellipses in Javascript and you’ve stumbled upon my article amongst all others, welcome! I’ll guide you through it.
What is the spread operator?
The spread operator looks like ...
and, in a nutshell, transforms an array (or object) into just its elements.
let arr = [1, 2, 3];
console.log(...arr); // 1 2 3
This syntax is new in ES6, so you may not have encountered it if you learned Javascript through outdated materials.
That’s nice, but how do I use it?
Glad you asked! Here are some ways that the spread operator makes our lives easier.
Copy an Array
The most basic (but not the easiest) way to copy an array is with a for
loop:
let arr = [1, 2, 3];
let copyOfArr = [];
for (let i = 0; i < arr.length; i++) {
copyOfArr.push(arr[i]);
}
console.log(copyOfArr); // [1, 2, 3]
A more savvy approach uses Array.slice()
:
let arr = [1, 2, 3];
let copyOfArr = arr.slice(0);
console.log(copyOfArr); // [1, 2, 3]
But, the easiest way is with the spread operator:
let arr = [1, 2, 3];
let copyOfArr = [...arr];
console.log(copyOfArr); // [1, 2, 3]
The spread operator takes the individual elements of arr and spreads (puts) them in our new array. Note that this is different from writing [arr]
:
// NOT WHAT WE WANT
let arr = [1, 2, 3];
let copyOfArr = [arr];
console.log(copyOfArr); // [[1, 2, 3]]
“But why can’t we just write let copyOfArr = arr
?”
Good question. The answer is, you absolutely can in some situations. HOWEVER, Javascript arrays and objects are passed by reference, not by value. This means that when we write let copyOfArr = arr
, our new variable copyOfArr
isn’t actually a copy of arr
— it’s a reference that points to arr
. So, if we change arr
, then copyOfArr
changes as well.
let arr = [1, 2, 3];
let copyOfArr = arr;
arr.pop();
console.log(copyOfArr); // [1, 2]
My guess is that if we wanted copyOfArr
to change every time arr
changes, we probably wouldn’t have made a copy in the first place.
Add an Element to the End of an Array
Now that we understand copying, these next examples will be easier. Suppose we want a new array with all the contents of arr
, except now with a new element at the end. This sounds like a job for Array.push()
.
let arr = [1, 2, 3];
let newArray = [...arr];
newArray.push(4);
console.log(newArray); // [1, 2, 3, 4]
However, as it turns out, the spread operator is so powerful we don’t even need Array.push()
! We’ll use the spread operator to create a new array with all the values of arr
, followed by our new element or elements:
let arr = [1, 2, 3];
let newArray = [...arr, 4];
console.log(newArray); // [1, 2, 3, 4]
Add an Element to the Beginning of an Array
You probably see where this is going:
let arr = [1, 2, 3];
let newArray = [0, ...arr];
console.log(newArray); // [0, 1, 2, 3]
Put an Array’s Values in the Middle of a New Array
We can combine the above two use cases:
let arr = [1, 2, 3];
let newArray = [0, ...arr, 4, 5];
console.log(newArray); // [0, 1, 2, 3, 4, 5]
Concatenate (Merge) Arrays
Combining arrays is easier than ever with spread syntax:
let oneTwo = [1, 2];
let threeFour = [3, 4];
let newArray = [...oneTwo, ...threeFour];
console.log(newArray); // [1, 2, 3, 4]
Get the Highest Value of an Array
I’ve seen programmers sort arrays from highest to lowest and then return the first element of the sorted array.
let values = [4, 1, 2, 5, 0];
let highestValue = values.sort((a, b) => b - a)[0];
console.log(highestValue); // 5
If you want to be fancy, here’s a solution that uses Array.reduce()
:
let values = [4, 1, 2, 5, 0];
let highestValue = values.reduce((acc, val) => (val > acc ? val : acc), 0);
console.log(highestValue); // 5
However, the most logical solution would be to use Math.max()
. The catch is, we have to pass in separate arguments in Math.max()
, so that means we can’t use Math.max()
on an array.
let values = [4, 1, 2, 5, 0];
let highestValue = Math.max(values);
console.log(highestValue); // NaN
If only there was some way to take an array and spread out its individual elements… oh wait!
let values = [4, 1, 2, 5, 0];
let highestValue = Math.max(...values);
console.log(highestValue); // 5
Now that we know about the spread operator, those other array methods seem roundabout by comparison.
Get the Lowest Value of an Array
Easy enough:
let values = [4, 1, 2, 5, 0];
let lowestValue = Math.min(...values);
console.log(lowestValue); // 0
Remove an Index from an Array
We could use Array.splice()
for this, but let’s pretend we don’t want to modify the original array.
let arr = [1, 2, 7, 3, 4, 5, 6, 7];
// The erroneous 7 is at index 2:
let i = 2;
let newArray = [...arr.slice(0, i), ...arr.slice(i + 1)];
console.log(newArray); // [1, 2, 3, 4, 5, 6, 7]
Update an Object
You may have a situation where you need to return an object with updated data, but without changing the original object. Take note, React developers: this will often be the case if you use Redux! Luckily, the spread operator works on objects. If we spread two objects into a new object and both objects share a key, the latter object will override the former with its value for that key. The result is a new object with updated data.
let youtubeVideo = {
title: "PB&J tutorial (GONE WRONG) (POLICE CALLED)",
likes: 2,
dislikes: 1000,
description: "Smash that like button and hit SUBSCRIBE!",
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
};
let updateData = {
dislikes: youtubeVideo.dislikes + 1
};
let updatedVideo = {
...youtubeVideo, ...updateData
};
console.log(updatedVideo); // same as youtubeVideo, but now with 1001 dislikes
Rest Syntax
Sometimes, three dots together is actually rest syntax, not spread syntax. Here’s the difference: if we use ...
on an array or object, it’s spread syntax and we get its individual values. But, if we use ...
on individual values, it’s rest syntax and we get an array or object. We can use rest syntax to condense a list of individual function parameters into an array:
function getAverage(...numbers) {
return numbers.reduce((acc, val) => acc + val) / numbers.length;
}
console.log(getAverage(5, 10, 50, 25, 35)); // 25
Rest Syntax with React
Let’s polish off this article with a hefty example. Let’s say we have some user data we want to render into a list. Any time we render a list in React, we need to give each list item a unique key
attribute. We use each user’s id
as the key
and write something like this:
This is easy enough, but what if we had even more detailed user data? Let’s imagine we also wanted to render our users’ hometown, email address, favorite food, height, weight, worst fear, etc, etc. Suddenly, we find ourselves destructuring and writing out a lot of props for the UserListItem
in our map()
function.
Luckily, we have an easier way. We’ll destructure the id
off each user and then use rest syntax to access the rest of the user’s properties. Then, we’ll spread those properties into our UserListItem
component.
Our code still works the same way, and now we have a more flexible and concise map()
function.
If we really want to take things all the way, we can use rest syntax within the definition of the UserListItem
component:
Hope this helps, and thanks for reading!
Top comments (0)