Ellipses, Three Dots, …, or Three Periods in Javascript — A Primer to the Spread Operator
Tags: javascript
Published: 2020-03-09 16:45:06


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:

{% gist https://gist.github.com/DanielJWagener/ee22516df11c5c20fa5f5ecfb73ed13e %}

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.

{% gist https://gist.github.com/DanielJWagener/a977555b7291385f108c37a455c3e289 %}

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:

{% gist https://gist.github.com/DanielJWagener/a7c0a4fd3ffa46243e9e4fa9f1d3ee87 %}

Hope this helps, and thanks for reading!

Follow me on [LinkedIn](https://www.linkedin.com/in/daniel-wagener-866868141/) and [GitHub](https://github.com/DanielJWagener/)