archives

« Bugzilla Issues Index

#3398 — Native implementation of reduceRight in JavaScript is wrong


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.