Spread operator for managing array order
In addition to being able to use the spread operator to avoid mutations by performing a shallow clone a great benefit to the spread operator is how they enable us to dynamically create arrays intuitively and with less code.
To use our previous example, let's say we are crafting an array of new menu ideas for our restaurant, but this time instead of just having lunch ideas to be used to be added to the array allMenuIdeas, we want to add in breakfast and dinner ideas:
const breakfastMenuIdeas = ["Buckwheat Pancakes"]; const dinnerMenuIdeas = ["Glazed Salmon", "Meatloaf", "American Cheeseburger"]; const allMenuIdeas = ["Harvest Salad", "Southern Fried Chicken"];
And say we want to add them in a certain order, say where breakfast ideas are put at the beginning. How do we do that? We haven't covered adding items to the beginning of an array, but we do that with the method .pop(). push adds items to the end of the array, pop to the beginning.
But unfortunately, like push, pop is a method that can mutate the original array. So is there a method that pushes items to the front of an array, that is non-mutating? At this point you might be wondering whether for every operation that we do to an array, to do it without mutations, do we have to know two methods and remember them all?
In fact there is a much more intuitive way to add and order our arrays, identical to the tool that we used for objects: the spread operator. Specifically the array spread operator.
It is written in the exact same way as the object spread, three dots, like so:
But what does the array spread operator do? Very simple--it converts an array to a list of elements. The name for it was chosen well, it spreads out our array.
We saw the benefit it provided in terms of easily performing a shallow clone, but let's dive deeper:
First of all, we can't use the array spread operator on its own. For example, if we tried to spread the allMenuIdeas array into nothing, like so:
...allMenuIdeas; // Uncaught SyntaxError: Unexpected token '...'
JS will give us a syntax error, acting as if it doesn't know what these three dots mean. And it doesn't, because we need to spread our list of elements into another array, which we'll put in a variable, otherMenuIdeas:
const otherMenuIdeas = [...allMenuIdeas]; console.log(otherMenuIdeas);
And we already know what this does--it creates a shallow clone of our allMenuIdeas array, but think about this: we can spread multiple arrays into a new array, so if we spread in the breakfastMenuIdeas before allMenuIdeas and separate it with a comma:
const otherMenuIdeas = [...breakfastMenuIdeas, ...allMenuIdeas]; console.log(otherMenuIdeas);
We see both arrays were spread into the new one and the order was preserved. So this answers our original problem--you don't have to remember push or pop or any other positional array methods. If you're reading other developer's code you may want to know what they do, but you don't have to rely on them yourself if you want to position items in arrays that you are creating.
So now let's return to our original problem. We want to put the breakfastMenuIdeas and dinnerMenuIdeas into allMenuIdeas in a certain order. To put breakfastMenuIdeas at the beginning, we just spread the entire array in like so:
const allMenuIdeas = [ ...breakfastMenuIdeas, "Harvest Salad", "Southern Fried Chicken", ]; console.log(allMenuIdeas);
And they're adding at the start of the new array and don't conflict with the existing elements. So you see how easy it become to merge arrays that already have data in them.
And then if we want to add dinnerMenuIdeas in at the end, we spread it there:
const allMenuIdeas = [ ...breakfastMenuIdeas, "Harvest Salad", "Southern Fried Chicken", ...dinnerMenuIdeas, ]; console.log(allMenuIdeas);
Intuitive nature of spread operator
What I love most about creating new arrays this way (and I’m sure you will, too) is that you can forget so many methods. You won’t need them anymore.
And on top of not needing to remember methods, when you write your arrays in this manner, you’re signaling your intention to return an array, since you're very clearly creating one in the array literal syntax with two brackets. If you were using an array method, the developer looking at your code would have to ask whether it returned a new array or not, as we saw in our last lesson with reduce.
So creating arrays is a breeze with the array spread, but what about something more advanced like updating an array element or deleting it?
spread operators just enable us to spread out our arrays. We can't use them to remove items. But what is interesting is that as we are spreading our arrays, we can use methods that do enable us to edit our array content.
When it comes to get a part of an array, a subarray, we can use a method called slice(). It is immutable like the array spread and it returns a brand new array. Slice works by passing it a starting index where you want to start slicing and a ending index where you want it to end.
So let's say that we're got all of our menu ideas in their final form, we're happy with all of the suggestions, but we want to make a couple of changes. We want to change the "Harvest Salad" to "Garden Salad" and we want to remove the "Meatloaf". How do we do that?
First let's assume we don't know where either the Harvest Salad or the Meatloaf items are in the array, we don't know their indices. So to find them, we can use a variant of the find method, but instead of returning the found item, it returns the index of the item, if it exists. So let's find both items by their name, where idea is equal to "Harvest Salad" and "Meatloaf". And we'll put them both in their own variables: saladIndex and meatloafIndex.
const allMenuIdeas = [ ...breakfastMenuIdeas, "Harvest Salad", "Southern Fried Chicken", ...dinnerMenuIdeas, ]; const saladIndex = allMenuIdeas.findIndex((idea) => idea === "Harvest Salad"); const meatloafIndex = allMenuIdeas.findIndex((idea) => idea === "Meatloaf");
Here we want to spread in allMenuIdeas, but just get the slice of the array from the 0th index, the beginning, all the way up to the salad, or the saladIndex. And for the next element, put in the text we want: 'Garden Salad', to completely replace the previous one, and then to get the last half of the array, slice from after the new element to the end. So we can get that by saying saladIndex + 1 and if the endpoint is not provided as the second argument, slice cuts through the rest of the array
const finalMenuIdeas = [ ...allMenuIdeas.slice(0, saladIndex), "Garden Salad", ...allMenuIdeas.slice(saladIndex + 1), ]; console.log(finalMenuIdeas);
And if we console.log this as finalMenuIdeas...
We see our reassembled array with 'Garden Salad' instead of 'Harvest Salad'. So that's how to use the array spread to update our array elements
And then based off of this example, try to solve one your own how to do the next step, how to delete items using slice and the array spread. Take a minute or two and see if you can the meatloaf entry that we have...
To start we'll just comment out our previous entry.
// const finalMenuIdeas = [...allMenuIdeas.slice(0, saladIndex), 'Garden Salad', ...allMenuIdeas.slice(saladIndex + 1)]; // console.log(finalMenuIdeas)
And again, we want to recreate our array by spreading it in and slicing from 0 to the meatloaf index, and then we don't have to provide any element if don't want it to be included, or deleted, so to speak. And then we can spread in the rest of the array from meatloafIndex + 1 to the end:
const finalMenuIdeas = [ ...allMenuIdeas.slice(0, meatloafIndex), ...allMenuIdeas.slice(meatloafIndex + 1), ]; console.log(finalMenuIdeas);
So with the spread operator, you know exactly what's going on. And that's because you're always going to be using it to create an array, immutably. Even if you didn't know what slice did, you know that we are just creating parts of a new array. But now you see it's flexibility with the ability to chain on any useful array method to it, to construct arrays exactly as you need.
So we covered the spread operator in great detail, but it's for a reason. It's very popular among developers for its readability and convenience, so you'll be seeing it a lot in any modern JS application as well as writing it a lot in your own code.
In summary, while we can think of the reduce method as the go-to tool for allowing us to transform the content of our existing arrays, the spread operator is arguably the most simple and intuitive way to create and order new arrays. Get comfortable with using it and you'll reap its benefits in your own applications.