Reversing a JavaScript array without .reverse() / code explanation

#1

I was working on a problem on Codewars.com and I stumbled upon an interesting solution on StackOverflow. Someone was asking how to reverse an array WITHOUT using .reverse().

Here is one solution someone wrote:

let reverse=a=>[...a].map(a.pop,a)

It helps me to look at it like this:
(but I still don't completely understand it)

let reverse = (a) => {
    return [...a].map(a.pop, a);
};

Original thread on StackOverflow

I have used the spread operator, but not in this situation. Why is it used here?
Why is .pop used without the parentheses? .pop()?
I also see that the person added the original array as an argument. I know that it is optional and can be done, but reading at MDN, I don't understand why.

I understand the big picture: map returns a new array and we are "popping" off the elements one at a time from the end of the array. This reverses the array. But, I really couldn't explain this to someone and I would like to understand.

So here's my question: Would anyone like to "walk me through this" and enlighten me as to what is going on here?

#2

Here is the code:

let reverse = (a) => [...a].map(a.pop, a);
// [5,4,3,2,1]

The [...a] uses the spread operator to make a copy of the array. Why is a copy being made? Consider this alternative code that doesn't use the spread operator.

let reverse = (a) => a.map(a.pop, a);
// [5,4,3,undefined,undefined]

When you do: reverse([1,2,3]) the map will run once for each item in the array.

With the 1st item in the array, the a.pop gives 3 and the array reduces down to [1,2]
With the 2nd item in the array, the a.pop gives 2 and the array reduces down to [1]
With the 3rd item in the array, it doesn't exist, so map gives undefined.

That leaves you with a result of [3,2,undefined] which is not what you want.

Instead, you want to pop from a copy of the array. There are older techniques to clone an array, such as using a.slice(0), which works:

let reverse = (a) => a.slice(0).map(a.pop, a);
// [5,4,3,2,1]

So [...a] is just a more modern ES6 technique of cloning an array, so that the original array doesn't get affected by the pop method.

#3

Note though that this function is even more destructive (so to say) than the array .reverse() method, in that clears the original array... mapping over a copy has absolutely no effect here. If you don't want to modify the original array, you'll have to pass a copy as the this parameter:

const reverse = a => a.map(Array.prototype.pop, [...a])

(You could also write a.pop here, but personally I like to be explicit when I'm using methods in the prototype directly... it changes the this context anyway. It's just a bit clearer this way.) :-)

#4

Thanks @m3g4p0p - that's something that I forgot to investigate.

The spread operator more properly belongs on the second parameter to the map function.

Here's the original code:

const reverse = a => [...a].map(a.pop, a);
// empties a

The problem as @m3g4p0p rightly pointed out, is that any assigned array that you reverse, end up being emptied because of the a.pop method.

Let's deal with that in a step-wide fashion. To prevent a.pop from destroying the array, you can make a clone for the second argument of the map function:

const reverse = a => [...a].map(a.pop, [...a]);

And now any variable that contains the array that you are reversing, is now preserved from being changed.
Because the mapping is now occurring on a clone of a, the initial clone is now not required:

const reverse = a => a.map(a.pop, [...a]);
1 Like
closed #5

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.