DEV Community

Cover image for Ellipses, Three Dots, …, or Three Periods in Javascript — A Primer to the Spread Operator
Daniel Wagener
Daniel Wagener

Posted on • Originally published at Medium

Ellipses, Three Dots, …, or Three Periods in Javascript — A Primer to the Spread Operator

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!

Follow me on LinkedIn and GitHub

Top comments (0)