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?
问题:
回答1:
var arr = [1, 2, 3];
arr.slice().reverse().forEach(function(x) {
console.log(x);
})
will print:
3
2
1
arr
will still be [1, 2, 3]
, the .slice()
creates a shallow copy.
回答2:
No, 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.
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:
Copy the array and reverse the copy, then use
forEach
on itUse
Object.keys
to get the indexes, reverse that, then useforEach
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
:
var a = ['one', 'two', 'three'];
a.slice().reverse().forEach(function(entry) {
snippet.log(entry);
});
snippet.log("Proof that a is not, itself, reversed: " +
JSON.stringify(a));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
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:
var a = ['one', 'two', 'three'];
Object.keys(a).reverse().forEach(function(index) {
snippet.log(a[index]);
});
snippet.log("Proof that a is not, itself, reversed: " +
JSON.stringify(a));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Side note: Here's what I mean about using filter
if you have non-element properties on your array:
var a = ['one', 'two', 'three'];
a.nonElementProperty = "foo";
Object.keys(a).filter(function(name) {
return String(+name) === name;
}).reverse().forEach(function(index) {
snippet.log(a[index]);
});
snippet.log("Proof that a is not, itself, reversed: " +
JSON.stringify(a));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
回答3:
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
回答4:
There is 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
.
回答5:
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);
}
回答6:
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
}
};
}
回答7:
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