103

Is there a way to loop backwards through an array using forEach (not any other kind of loop, I know how to do with with a for / standard ways) and without actually reversing the array itself?

3
  • 1
    what is it you are trying to do, exactly? As worded, this is an XY question, asking about a specific potential solution to an unknown issue. Commented Sep 20, 2015 at 19:31
  • 3
    The possible use case is if someone is maintaining 'layers' of an image editor, they could all be part of an array, and looping through them backwards and forwards could be vital. Commented Sep 29, 2017 at 5:31
  • @MuhammadbinYusrat - Sure, but you can do that using a simple for loop. The question specifically rules out anything other than forEach. Commented Mar 19, 2024 at 8:58

14 Answers 14

127

let arr = [1, 2, 3];

arr.slice().reverse().forEach(x => console.log(x))

will print:

3
2
1

arr will still be [1, 2, 3], the .slice() creates a shallow copy.

Sign up to request clarification or add additional context in comments.

3 Comments

This is very inefficient compared to just using a for loop and iterating backward. If OP really needs a function they can add a forEachReverse function onto the array prototype that uses a for loop internally.
@kennsorr OP asked "without actually reversing the array itself", and .reverse() modifies the array instance it is called on. The .slice() is a quick way to create a new copy of the array on the fly.
Except you shouldn't be "creating a new copy of the array on the fly!" Memory allocation is one of the slowest things we can do in a program. Copying an array and running a reverse on it just to iterate over it backwards is totally ridiculous. PLEASE just use a for loop.
65

Just use a for loop. Start at the end of the array and go backwards from there.

const array = ['blastoff', 1, 2, 3];

for (let index = array.length - 1; index >= 0; index--) {
  const element = array[index];
  console.log(element);
}

4 Comments

Oh... i lost faith into devs after reading other answers, but here it is, proper and efficient solution
quoting the question: "not any other kind of loop, I know how to do with with a for / standard ways", this answer is not appropriate.
@Guntram this is the fastest way. The only reason I can think of for not using a for loop for this purpose is that the person who asked the question was trying to cheat on their CS homework. :D
I think the question asker was looking for some kind of standard forEachReverse() that they hoped existed. The real answer to their question is "it doesn't exist sorry".
59

Since ECMAScript 2023 there are some array methods that iterate backwards, like for instance findLast. As long as in the callback you don't return a truthy value, it will visit all elements from end to start:

const array = ['alpha', 'beta', 'gamma'];

array.findLast(elem => console.log(elem));

As commented below, be aware that find* methods also visit empty slots, while forEach doesn't. This could be important when you are dealing with a sparse array. To exclude visiting empty slots, add a check that the iterated index exists:

array.findLast((elem, i) => {
    if (i in array) console.log(elem);
});

Even before ECMAScript 2023 there was a similar array method that has a reverse counter part, reduce comes together with reduceRight:

const array = ['alpha', 'beta', 'gamma'];

array.reduceRight((_, elem) => console.log(elem), null);

When using it for the requested purpose, make sure to provide a second argument. It can be null or anything else. Also note that the callback function has as first argument the accumulator, which you don't need for this purpose.

If including a library is an option:

Lodash: forEachRight.

10 Comments

The only answer that isn't terrible or obvious. Nice one. I probably still wouldn't use it over a simple reverse for loop though - too confusing to read.
Awesome answer. In contrast to most other solutions it does not manipulate the array it operates on in place!
More recently (I don't know when it was added), how about arr.toReversed().forEach(...) ?
@Richard, that works but at the cost of creating a second array.
@trincot I doubt that that's a concern for the vast majority of people. Even if the array is massive today's browsers will (probably) handle it quickly.
|
27

You've asked (my emphasis):

Is there a way to loop backwards through an array using forEach (not any other kind of loop, I know how to do with with a for / standard ways) and without actually reversing the array itself?

No, there isn't. forEach only processes forward through the array. So you'd have to do something else, which you've said in your question was out of scope.

You could use a for loop, of course, or write your own forEachRight, but again your question was very clear about requiring forEach, specifically. So the answer to the question is: No.


I would just use a for loop for this, but you've said you want to use forEach. The only way to use forEach without making a copy of the array that I can think of is to use a Proxy. I don't recommend it, not least because Proxy objects are notoriously slow, but it looks like this:

const rexAllDigits = /^\d+$/;
function createReversedGetProxy(a) {
    const pa = new Proxy(a, {
        get(target, key, receiver) {
            if (typeof key !== "string" || !rexAllDigits.test(key)) {
                return Reflect.get(target, key, receiver);
            }
            const index = target.length - key - 1;
            return Reflect.get(target, index, receiver);
        },
    });
    return pa;
}

Then use createReversedGetProxy(a).forEach(/*...*/);

Live Example:

const rexAllDigits = /^\d+$/;

function createReversedGetProxy(a) {
    const pa = new Proxy(a, {
        get(target, key, receiver) {
            if (typeof key !== "string" || !rexAllDigits.test(key)) {
                return Reflect.get(target, key, receiver);
            }
            const index = target.length - key - 1;
            return Reflect.get(target, index, receiver);
        },
    });
    return pa;
}

const a = ["one", "two", "three"];
createReversedGetProxy(a).forEach((v) => console.log(v));
console.log("Proof that a is not, itself, reversed: " +
            JSON.stringify(a));

Bending the question rules a bit, you could write your own forEachRight. In general it's best to avoid modifying built-in prototypes, so this would be best as a freestanding function you pass the array into:

function forEachRight(array, callback) {
    for (let index = array.length - 1; index >= 0; --index) {
        if (index in array) {
            callback(array[index], index, array);
        }
    }
}

function forEachRight(array, callback) {
    for (let index = array.length - 1; index >= 0; --index) {
        if (index in array) {
            callback(array[index], index, array);
        }
    }
}
const a = ["one", "two", "three"];
forEachRight(a, (v) => console.log(v));
console.log("Proof that a is not, itself, reversed: " +
            JSON.stringify(a));

That said, you could put it on Array.prototype, it's just not generally good practice; if you do, be sure to make it non-enumerable by using Object.defineProperty or similar, not simple assignment:

Object.defineProperty(Array.prototype, "forEachRight", {
    value(callback) {
        for (let index = this.length - 1; index >= 0; --index) {
            if (index in this) {
                callback(this[index], index, this);
            }
        }
    },
    enumerable: false,   // false is the default, but including here for emphasis
    configurable: true,
    writable: true,
});

Object.defineProperty(Array.prototype, "forEachRight", {
    value(callback) {
        for (let index = this.length - 1; index >= 0; --index) {
            if (index in this) {
                callback(this[index], index, this);
            }
        }
    },
    enumerable: false,
    configurable: true,
    writable: true,
});
const a = ["one", "two", "three"];
a.forEachRight((v) => console.log(v));
console.log("Proof that a is not, itself, reversed: " +
            JSON.stringify(a));

Alternatively, I can think of two options which just use precursors to using forEach (so, they don't use a for loop or other kind of loop). I don't know if those would be out of scope or not, so here they are:

  1. Copy the array and reverse the copy, then use forEach on it

  2. Use Object.keys to get the indexes, reverse that, then use forEach on it (which will loop through the indexes, not the values, but then we can look them up)

Here's #1:

slice copies the array (shallow copy, so not likely to be expensive), then we reverse it, then forEach:

const a = ["one", "two", "three"];
a.slice().reverse().forEach(function(entry) {
    console.log(entry);
});
console.log("Proof that a is not, itself, reversed: " +
            JSON.stringify(a));

Here's #2:

We use Object.keys to get the array indices (using filter if you store non-element properties in your arrays), reverse that, and then loop through the result:

const a = ["one", "two", "three"];
Object.keys(a).reverse().forEach(function(index) {
    console.log(a[index]);
});
console.log("Proof that a is not, itself, reversed: " +
            JSON.stringify(a));

Both of those create temporary arrays, so the Proxy approach may be better from a memory perspective, but slower at runtime. None of these is better than a simple for, though.

Just for fun, here's a benchmark:

const rexAllDigits = /^\d+$/;

const tests = Array.from({ length: 4 }, (_, index) => {
    const a = Array.from({ length: rnd(index % 2 === 0 ? 4000 : 400) }, () =>
        rnd(100000)
    );
    const sum = a.reduce((a, v) => a + v, 0);
    return {
        a,
        expected: sum,
    };
});

function createReversedProxy(a) {
    const pa = new Proxy(a, {
        get(target, key, receiver) {
            if (typeof key !== "string" || !rexAllDigits.test(key)) {
                return Reflect.get(target, key, receiver);
            }
            const index = target.length - key - 1;
            return Reflect.get(target, index, receiver);
        },
    });
    return pa;
}

function forEachRight(array, callback) {
    for (let index = array.length - 1; index >= 0; --index) {
        if (index in array) {
            callback(array[index], index, array);
        }
    }
}

function rnd(max) {
    return Math.floor(Math.random() * max);
}

function withProxy(a, expected) {
    let sum = 0;
    createReversedProxy(a).forEach((v) => (sum += v));
    if (sum !== expected) {
        throw new Error(`Error in test, got ${sum}, expected ${expected}`);
    }
}

function withSlice(a, expected) {
    let sum = 0;
    a.slice()
        .reverse()
        .forEach((v) => (sum += v));
    if (sum !== expected) {
        throw new Error(`Error in test, got ${sum}, expected ${expected}`);
    }
}

function withForEachRight(a, expected) {
    let sum = 0;
    forEachRight(a, (v) => (sum += v));
    if (sum !== expected) {
        throw new Error(`Error in test, got ${sum}, expected ${expected}`);
    }
}

// This goes forward, not backward; it's just here to give us something to compare to
function withForEach(a, expected) {
    let sum = 0;
    a.forEach((v) => (sum += v));
    if (sum !== expected) {
        throw new Error(`Error in test, got ${sum}, expected ${expected}`);
    }
}

// Out of scope for the question, but again, something to compare to
function withFor(a, expected) {
    let sum = 0;
    for (let index = a.length - 1; index >= 0; --index) {
        sum += a[index];
    }
    if (sum !== expected) {
        throw new Error(`Error in test, got ${sum}, expected ${expected}`);
    }
}

const btnRun = document.getElementById("btnRun");
btnRun.addEventListener("click", () => {
    btnRun.disabled = true;

    setTimeout(() => {
        console.log("Running");
        
        const suite = new Benchmark.Suite();

        suite
            .add("forEach backward with proxy", () => {
                for (const { a, expected } of tests) {
                    withProxy(a, expected);
                }
            })
            .add("forEach backward with slice", () => {
                for (const { a, expected } of tests) {
                    withSlice(a, expected);
                }
            })
            .add("forEachRight", () => {
                for (const { a, expected } of tests) {
                    withForEachRight(a, expected);
                }
            })
            .add("forEach forward", () => {
                for (const { a, expected } of tests) {
                    withForEach(a, expected);
                }
            })
            .add("Backward with for", () => {
                for (const { a, expected } of tests) {
                    withFor(a, expected);
                }
            })
            .on("error", () => {
                console.log("*** Error ***");
            })
            .on("cycle", (...args) => {
                console.log("...");
            })
            .on("complete", function () {
                console.log("Results (higher cycles/second = faster):");
                const results = Array.from(this);
                let longest = 0;
                for (const { name } of results) {
                    longest = Math.max(longest, name.length);
                }
                for (const { name, hz } of results) {
                    console.log(`${name.padEnd(longest)}: ${hz} cycles/second`);
                }
                console.log("Fastest is: " + this.filter("fastest").map("name"));
                btnRun.disabled = false;
            })
            // run async
            .run({ async: true });
    }, 50);
});
.as-console-wrapper {
    max-height: calc(100% - 2rem) !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/2.1.4/benchmark.min.js"></script>
<input type="button" id="btnRun" value="Run Test">


Side note: Here's what I mean about using filter if you have non-element properties on your array:

const a = ["one", "two", "three"];
a.nonElementProperty = "foo";
Object.keys(a).filter(function(name) {
    return String(+name) === name;
}).reverse().forEach(function(index) {
    console.log(a[index]);
});
console.log("Proof that a is not, itself, reversed: " +
            JSON.stringify(a));

7 Comments

How is your key reversal approach better than just doing reverse on the collection itself?
@victor175: The OP said he didn't want to reverse the array ("...without actually reversing the array itself").
@ashleedawg- I don't understand your comment I'm afraid. I didn't say that slice().reverse() reverses the original -- in fact, I say exactly the opposite above -- see the first code snippet. So...?
With this answer you get the original index instead of the reversed index, so it's more useful
@AlexK - Oh, I wholeheartedly agree. The question explicitly rules out what I'd do (a simple for), requiring forEach in the solution ("...using forEach (not any other kind of loop..."). But here nearly nine years later, I've thought of a solution using forEach without copying the array which I've added to the answer. (It was possible back in 2015, too, though Proxy was brand-new then. :-) ) Again: Would I do that? No. But that's the question the OP asked.
|
8

As yet the browsers do not seem to have optimised the Array.forEach function. With not much effort you can write a simple polyfill that out performs the Array.forEach method by at least 10 to 1.

So you can create your own Array.revEach and have it outperform the native Array.forEach, thought I hope that the browsers address the very slow performance of Array.forEach soon and make the need to polyfill actual existing methods not necessary.

For Array.revEach out performs Array.forEach running 17 times faster on "Chrome 46.0.2490.22 beta-m"

if (Array.prototype.revEach === undefined) { 
    Object.defineProperty(Array.prototype, 'revEach', { 
        writable : false,
        enumerable : false,
        configurable : false,
        value : function (func) {
            var i;
            var len = this.length-1;
            for (i = len; i >= 0; i--) {
                func(this[i], i, this);
            }
        }
    });
}

Just to add the actual official polyfill modified to reverse. Comments show my changes.

// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
// Modified by Blindman67 to revEach
if (!Array.prototype.revEach) {   // name changed
  Array.prototype.revEach = function(callback, thisArg) { // name changed
    var T;  // k defined where len was
    if (this == null) {
      throw new TypeError(' this is null or not defined');
    }
    var O = Object(this);
    var k = (O.length >>> 0)-1;  // set k (counter) ToUint32
                                 // len var removed
    if (typeof callback !== "function") {
      throw new TypeError(callback + ' is not a function');
    }
    if (arguments.length > 1) {
      T = thisArg;
    }
    while (k >= 0) {  // reverse condition
      var kValue;
      if (k in O) {
        kValue = O[k];
        callback.call(T, kValue, k, O);
      }
      k--;  // dec counter
    }
  };
}

Comments

6

array.forEach has 3 parameters. You can use these to effectively forEach backward.

var arr = [1, 2, 3];

    arr.forEach(function(x, index, the_array) {
        let x_prime = the_array[the_array.length-1-index]
        console.log(x_prime);
    })

will print

3
2
1

1 Comment

This is just a really convoluted way of doing a normal for loop.
5

This can be accomplished relatively concisely using the reverse method, the forEach method, and (if using ES6) the arrow function

var someArray = ["a","b","c","d"];
someArray.reverse().forEach(arrayItem =>
    console.log(arrayItem)
)

If you are not using ES6, the solution is about the same, just without the arrow function.

var someArray = ["a","b","c","d"];
someArray.reverse().forEach(function(arrayItem) {
    console.log(arrayItem)
})

Both will print to the console:

d
c    
b
a

3 Comments

Actually .reverse() will reverse someArray, so you need to do someArray.reverse() again or it will end to undesirable bugs.
@V.Dalechyn Two reverse()s do not make a right! This is a terrible answer.
@Timmmm it's been almost two years, thanks for the response :D
3

There are many ways to achive this task.

  1. Firsly we can use Array.reduceRight() method

[1,2,3,4,5].reduceRight((total,item) => {
 console.log(item);
 // Do somthing here and remember the return statement
 return item;
},0)


Output: 5,4,3,2,1

the reduceRight method traverse an array in right to left manner and we can get advantage of it.

,but always keep in mind that you have to return something to keep going this loop until the length is reached.

  1. As a second method we can use Array.reverse()

this method first format the array in reversed manner then returns it, and now you can iterate in reverse manner.

[1,2,3].reverse().map(n => console.log(n));
Output: 3,2,1

But the disadvantage of this method is that if you have thousands of entries in array then it may affect your performance.

  1. Third and the most easiest way it to use classical for loop

let array = [1,2,3,4,5];
let start = array.length;
for(;start >= 0;start--){
  // travese in right to left manner
  console.log(array[start])
}

1 Comment

array[array.length] is undefined, so what you probably meant "let start = array.length - 1"
2

What about:

const array = [1, 2, 3];
const revArray = [...array].reverse() // won't affect the original array
revArray.forEach(x => console.log(x)) // 3, 2, 1

One liner:

[...array].reverse().forEach(x => console.log(x)) // 3, 2, 1

With index var:

[...array].reverse().forEach((val, index) => {
  const revIndex = array.length - i - 1
  console.log(revIndex) // 0, 1, 2
  console.log(revIndex) // 2, 1, 0
  console.log(val) // 3, 2, 1
})

2 Comments

Works fine if the array isn't needed in the original order anymore, which the question here needs. To recite mdn docs Careful: reverse is destructive -- it changes the original array.
Also in this case reverse() will add additional time complexity ? we could iterate array in reverse order instead.
1

My solution is to use a "readable" for loop:

for (let index = array.length; index > 0; ) {
    index--;
    // do stuff
}

Personal preference. For me, it adds a certain symmetry to forward loops. Lots of coders go with for (let index = array.length - 1; index >= 0; index--) {...}

2 Comments

Interesting way, not sure it add a certain symmetry, but like you say that's personal preference. But one slight bug, I'm pretty sure you meant to do index -- :) Your not a million miles from just using a while either, although scope is slightly different. -> let index = array.length; while (index > 0) { index --; /*doStuff*/ }
thanks for the catch, updating! And it's essentially a while loop, absolutely.
1

Here's a version that uses a generator function:

function* reverseIterate(array) 
{
  for (let i = array.length - 1; i >= 0; i--)
    yield array[i];
}

let foods = ["apple", "banana", "cherry", "date", "eggplant", "figs"];

for (let item of reverseIterate(foods))
  console.log(item);

1 Comment

Oh yes, that's it!
1

Since July 2023 you can use array.toReversed().forEach().
This will not affect the original array since .toReversed() returns a new array.

const list = ['Hello', 'World'];

list.toReversed().forEach(item => console.log(item))

Read more about it here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toReversed

Comments

0

If anyone is interested, here are time tests for the viable answers (might take a second to run):

  • findLast, reduceRight - @trincot
  • reverseForLoop -> @Alex K
  • sliceReverse -> @naartjie
  • objectKeys -> @T.J. Crowder
  • indexingForEach -> @grabbag

var results = [];
var loops = [];
const iterations = 5;

function testLoop(name, loop, iteration) {
    if(!results[iteration])
      results[iteration] = []
    var array = []
    for(let i = 0; i < 10000; i++) {
        array.push(i);
    }
    var start = window.performance.now();
    loop(array);
    var end = window.performance.now();
    results[iteration].push(name + " took " + (end - start) + "ms");
}

loops.push({name: 'findLast', func: array => array.findLast(elem => console.log(elem))});

loops.push({name: 'reverseForLoop', func: array => {
    for(let i = array.length - 1; i >= 0; i--) {
        console.log(array[i]);
    }
}});

loops.push({name: 'sliceReverse', func: array => {
    array.slice().reverse().forEach(x => console.log(x));
}});

loops.push({name: 'reduceRight', func: array => {
    array.reduceRight((_, elem) => console.log(elem), null);
}});

loops.push({name: 'objectKeys', func: array => {
    Object.keys(array).reverse().forEach(function(index) {
        console.log(array[index]);
    });
}});

loops.push({name: 'indexingForEach', func: array => {
    array.forEach(function(x, index, the_array) {
        let x_prime = the_array[the_array.length-1-index]
        console.log(x_prime);
    })
}});

for(let i = 0; i < iterations; i++) {
    for(let j = 0; j < loops.length; j++) {
        testLoop(loops[j].name, loops[j].func, i);
    }
}

console.clear();
console.log("\n")

for(let i = 0; i < results.length; i++) {
  console.log("iteration "+ (i+1))
   for(let j = 0; j < results[i].length; j++) {
       console.log(results[i][j]);
   }
   console.log("\n")
}

Comments

-3

You can also do it this way

let arr = [1, 3, 5, 7, 9];

arr.forEach(value => {
    console.log(value);
})

let reversed = new Array(arr.reverse());
console.log("\n\nReversed: ");

reversed.forEach(value => {
    value.forEach(val => {
        console.log(val)
    })
})

1 Comment

The extra nexting of forEach doesn't give any benefit. As mentioned before arr will be reversed in place causing unexpected side effects.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.