For an associative operation f
over the elements of 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.
So, my questions are:
- Doesn't it indeed make more sense for
reduceRight
to be implemented the way I did? - Why is the native
reduceRight
not implemented the way I did?