For an associative operation `f` over the elements of an array `a`, the following relation should hold true: `a.reduce(f)` should be equivalent to `a.reduceRight(f)`.
Indeed, it does hold true for operations that are both associative and commutative. For example:
var a = [1,2,3,4,5,6,7,8,9,0];
alert(a.reduce(add) === a.reduceRight(add));
function add(a, b) {
return a + b;
}
However it doesn't hold true for operations that are associative but not commutative. For example:
var a = [[1,2],[3,4],[5,6],[7,8],[9,0]];
alert(equals(a.reduce(concat), a.reduceRight(concat)));
function concat(a, b) {
return a.concat(b);
}
function equals(a, b) {
var length = a.length;
if (b.length !== length) return false;
for (var i = 0; i < length; i++)
if (a[i] !== b[i]) return false;
return true;
}
We need to flip the arguments of `f` for `reduceRight` to make them equivalent:
var a = [[1,2],[3,4],[5,6],[7,8],[9,0]];
alert(equals(a.reduce(concat), a.reduceRight(concatRight)));
function concat(a, b) {
return a.concat(b);
}
function concatRight(b, a) {
return a.concat(b);
}
function equals(a, b) {
var length = a.length;
if (b.length !== length) return false;
for (var i = 0; i < length; i++)
if (a[i] !== b[i]) return false;
return true;
}
This makes me believe that the native implementation of `reduceRight` is wrong.
I believe that the `reduceRight` function should be implemented as follows:
var REDUCE_ERROR = "Reduce of empty array with no initial value";
Array.prototype.reduceRight = function (f, acc) {
var a = this, length = a.length;
if (arguments.length < 2) {
if (length !== 0) var right = a[--length];
else throw new TypeError(REDUCE_ERROR);
} else var right = acc;
while (length !== 0) right = f(a[--length], right, length, a);
return right;
};
Since `right` represents the previous value (right-hand side value), it makes sense to make it the second parameter of the function `f`. The current value represent the left-hand side value. Hence, it makes sense to make the current value the first parameter of the function `f`. This way, even for non-commutative associative operations, the aforementioned relation holds true.
My suggestions are:
1. Either fix this in ECMAScript Harmony (not backwards compatible and will in all probability break existing code).
2. Or provide a separate native operation which does the right thing. You could call it `reduceR` (shorter and sweeter).
Linked StackOverflow question: http://stackoverflow.com/q/27252657/783743
We can't break reduceRight. To propose a new API, see https://github.com/tc39/ecma262/blob/master/CONTRIBUTING.md.