Most of the time when you’re looping over an array of elements, you’ll do it sequentially. However, I recently needed to iterate through an array of objects in reverse just using a plain old for loop.
This will not be a highly informative or groundbreaking post but, I thought I’d share this in case someone wants to solve the same problem and might be confused with the many different ways you can loop over an array in reverse.
I had an idea in mind, I knew Array.reverse
would be the ideal candidate but I still Googled to see if smarter developers than me figured something better out.
Turns out, there are a lot of scary alternatives to looping an array in reverse (mostly on StackOverflow). Some people are proponents of decrementing and using while loops, others had different ideas. Why not just use a function that’s existed since the dawn of Javascript?
var myItems = [
{name: 'Dwayne'},
{name: 'Rob'},
{name: 'Marie'},
{name: 'Sarah'},
{name: 'Emma'},
{name: 'James'}
];
var itemsToIterate = myItems.slice(0).reverse();
for (var i = 0, len = itemsToIterate.length; i < len; i++) {
var item = itemsToIterate[i];
}
In our example, we take an array of items and then we use slice
to make a copy of our array starting at its first index (zero). Then we call reverse
on the cloned array.
I said efficient in the title, but I haven’t benchmarked anything. However, we are using a barebones for loop and you don’t really get much faster than that. Sometimes commonsense and readability beat microoptimisating.
The reason we copy the array is so we don’t modify the original array. Using slice allows us to effectively clone the array and gives us a new instance, there are other ways of doing the same thing but I find this way is the cleanest.
Without slice, you’ll be modifying the provided value to our function and might produce an unintended result doing so. I tend to keep my functions pure for this kind of thing, nothing that gets input should be modified.
Thanks to reverse flipping our array upside down, we iterate like we would normally. Breaking out the reverse functionality into a function might also be a great idea. This would allow us to easily test our functionality from within a unit test.
function reverseArray(array) {
return array.slice(0).reverse();
}
One thing to keep in mind is for my use-case, slice
worked — if you’re dealing with arrays containing object references or nested arrays or complex objects, slice
does not do a deep copy.
Lodash has some great methods for doing recursive and deep cloning of arrays and collections if you need that kind of power. Post your thoughts and suggestions in the comments below.
Hi Dwayne,
I was wondering, why not just do it like:
for( var i = myItems.length-1; i >= 0; i– ) {
console.log(myItems[i]);
}
Hi Martijn,
That would work too. The solution I ended up using just feels more simple, I find decrementing to look a bit harsh on the eyes. Whereas, the reverse method just looks cleaner and it’s easier to understand what’s going on.
Hi Dwayne,
I also wonder if this would work…
var itemsToIterate = myItems.slice(0);
while(itemsToIterate.length != 0 )
var item = itemsToIterate.pop();
Efficient?
How about?
var i = myItems.length;
while (i–) {
console.log(myItems[i].name);
}
“Efficiently Looping” you’re cloning an entire array, which is probably one of the least efficient operations you could be doing to achieve this.
Layl, did you even read the article? Dwayne clones the array ONCE. This is just so the original array is not mutated, so you’re making changes to a new array. It’s a standard thing many of us do. You don’t get anymore efficient than a for loop.
The for loop’s efficiency is dwarfed by the cost of allocating the memory for a new array and copying over all the values. With a small array and with most javascript applications this cost is going to be negligible, but it’s not “Efficient”. A simple decrementing for loop avoids the cost of this all-together. This is certainly an easily understandable solution, but it’s not efficient.
Hi, the fastest way is to use while instead of for like this:
function reverseLoop(arr = [], fn = (item) => {console.log(item);}) {
let l = arr.length;
while(l–) {
fn(arr[l]);
}
}
For: 765,714 op/sec.
While: 439,365,249 op/sec.
Seeing this humongous difference in performance you could tweak the previous function a bit like this:
function loop(arr, fn = (item) => {console.log(item);}, reverse=true) {
if(!arr || !arr.length) return;
if(!!reverse) arr.reverse();
let l = arr.length;
while(l–) {
fn(arr[l]);
}
}
YamiteruXYZ I don’t think yours is any better, I’ve used the performance.now() API to mesure execution time:
1- using a normal while loop:
Call to while took 2.299999992828816 milliseconds.
2- using a normal for loop:
Call to for took 2.3999999975785613 milliseconds.
3-using your reverseLoop():
Call to reverseLoop took 2.7999999874737114 milliseconds.
4-using your loop():
Call to loop took 10.399999999208376 milliseconds.
So simply put it’s way better to just use a while or a for loop.