How to shift array position in vanilla JavaScript

Knut Melvær - Dec 19 '18 - - Dev Community

I was helping a user over at sanity.io yesterday doing some content migration, where they wanted to take the last element of an array and put it in the first position. Every time I get a question like this, I like to see if I manage to solve it without going to any libraries. Both because I don't want to introduce more dependencies for the person with the question, but most of all, it's an opportunity for some mental exercise.

So, how to change an array like this:

["Cersei", "Joffrey", "Ilyn", "The Mountain", "The Hound", "Melisandre", "Beric", "Thoros", "Tywin", "Meryn", "Walder"]

to this?

["Walder", "Cersei", "Joffrey", "Ilyn", "The Mountain", "The Hound", "Melisandre", "Beric", "Thoros", "Tywin", "Meryn"]

My first instinct was to make a new array just targeting the last element with the list length subtracted by 1, and then spreading the rest of the array using splice. Push the green run button to run the code:

const theList = ["Cersei", "Joffrey", "Ilyn", "The Mountain", "The Hound", "Melisandre", "Beric", "Thoros", "Tywin", "Meryn", "Walder"] const newList = [theList[theList.length - 1], ...theList.splice(0, theList.length - 1)] console.log(newList)

Some of you will already have guessed why this is a bad idea, and some of you will be like me and always mix up splice and slice. Let’s see what happens with theList when we run the code above:

const theList = ["Cersei", "Joffrey", "Ilyn", "The Mountain", "The Hound", "Melisandre", "Beric", "Thoros", "Tywin", "Meryn", "Walder"] const newList = [theList[theList.length - 1], ...theList.splice(0, theList.length - 1)] // check output console.log(newList) // check output console.log(theList)

Not ideal (unless you're Arya). Replacing splice with slice leaves the original array intact, and will not mutate it (which can lead to a mountain of problems):

const theList = ["Cersei", "Joffrey", "Ilyn", "The Mountain", "The Hound", "Melisandre", "Beric", "Thoros", "Tywin", "Meryn", "Walder"] const newList = [theList[theList.length - 1], ...theList.slice(0, theList.length - 1)] console.log(newList) console.log(theList)

This leaves us with exactly what we want.

Another way to go about it is to use the Array.prototype.reduce-method, which to me feel a bit cleaner. Also, .reduce is worth investing time into learning.

const theList = ["Cersei", "Joffrey", "Ilyn", "The Mountain", "The Hound", "Melisandre", "Beric", "Thoros", "Tywin", "Meryn", "Walder"] const newList = theList.reduce( (accumulatedArray, currentItem, index, originalArray) => index < originalArray.length - 1 ? [...accumulatedArray, currentItem] : [currentItem, ...accumulatedArray], [] ) console.log(newList) console.log(theList)

This can, of course, be made into a function. Tempting as it may be, I don't recommend using an anonymous arrow (=>) function for this, as it doesn't show up with a name in the console stack trace if something wrong occurs:

const theList = ["Cersei", "Joffrey", "Ilyn", "The Mountain", "The Hound", "Melisandre", "Beric", "Thoros", "Tywin", "Meryn", "Walder"] function shiftLastToFirst (theArray) { return theArray.reduce( (accumulatedArray, currentItem, index, originalArray) => { return index < originalArray.length - 1 ? [...accumulatedArray, currentItem] : [currentItem, ...accumulatedArray] }, [] ) } shiftLastToFirst(theList)

Now, I'm certain there are many smarter ways of doing this. And the reason I'm writing this up and putting it here is that I want to learn about them.

How would you go about solving this?

. . . . . . . . . . . . . . . . . . . . . . . . .