Proposal: mapRight

Proposal: mapRight

Overview:
The proposal presents mapRight, an array method that processes elements from right to left, analogous to how reduceRight complements reduce.

Currently, JavaScript provides map to iterate over an array from left to right. However, there is no built-in method to map elements in reverse order. When engineers need to achieve this, they typically reverse the array first and then apply map, leading to an unnecessary O(n) time complexity due to the additional array reversal.

Reasoning:
map: iterates from left to right
reduce: iterates from left to right
reduceRight: iterates from right to left

However, there is no mapRight counterpart to map that performs mapping from right to left.

Engineers often work around this by manually reversing the array before calling map. For example below:
const arr = [1, 2, 3, 4, 5];
const result = arr.toReversed().map(x => x + 1); // [6,5,4,3,2]

This introduces an extra linear time and space complexity i.e. O(N) operation and might be inefficient for data intensive applications like AI.

We could also use reduceRight to be forced to behave like mapRight, it is not ideal solution.

Although engineers can achieve the same result using a for loop, but it is less readable in function style code. Introducing mapRight enhances readability and expressiveness by providing a more declarative approach.

Proposal:
Proposal is to introduce Array.prototype.mapRight, which behaves like map, but processes elements from right to left.

array.mapRight(callbackFunction(element, index, array), thisArg)

const arr = [1, 2, 3, 4, 5];
const result = arr.mapRight (x => x + 1);

console.log(result); // [6,5,4,3,2]

The callback function receives the same parameters as map:

  • element – The current element being processed in the array.
  • index – The index of the current element being processed in the array.
  • array – The array mapRight was called upon.

Many AI applications, such as time-series data processing, NLP, and neural networks, require reverse-order traversal.

Advantages:

  • Eliminates the need for .reverse() or .toReversed(), saving O(N) complexity.
  • Provides a direct functional counterpart to map, just as reduceRight

Conclusion:
Integrating mapRight into JavaScript will enhance efficiency and readability when processing arrays from right to left.

What's a concrete use case?

While reviewing PRs, I have observed multiple times that when engineers need to render data in reverse order, they typically use .reverse().map() or .toReversed().map() . This pattern is common in practical use cases like rendering timestamps in descending order or displaying history logs.

That's not how big O works. Yes, reverse adds an extra O(n) step, but doesn't increase complexity of the whole operation, that remains O(n).

reduceRight's raison d'etre is that it avoids making a reversed copy, in cases where you want to keep the original array unchanged. In other words, space complexity for reduceRight is O(1), but for toReversed().reduce() it is O(n).

You can avoid making an extra copy by reversing the result, instead of the original:

const result = arr.map(fn).reverse();

This needs the same amount of memory as arr.mapRight(fn). As for the time... I would argue that the constant hiding inside O(n) is much bigger for map than for reverse.

2 Likes

There's a good reason for reduceRight to exist (similar to foldl/foldr in most languages). reduce and reduceRight correspond to left-associated and right-associated call trees: f(f(f(...), arr[n - 1]), arr[n]) for reduce vs f(arr[0], f(arr[1], f(...))) for reduceRight. You get entirely different computation results, as long as your f is not commutative. reduceRight is not equivalent to reduce . reverse, which would give you f(f(f(...), arr[1]), arr[0]) instead. It's not like reduceRight can be easily implemented by composing a few array methods.

2 Likes

Tbh I would have expected a such-named method to still return the results in the normal order, and just to process the elements in the reverse order (from the right). But that's generally a bit pointless, as map is expected to be order-independent (sans exceptions, maybe).

I think your use case should rather be solved using reverse iteration, see Use Cases Β· Issue #1 Β· tc39/proposal-reverseIterator Β· GitHub / GitHub - leebyron/ecmascript-reverse-iterable: ReverseIterable Interface Spec Proposal / Array.prototype.reversed() (+Map, Set). These would allow you to write something like

const arr = [1, 2, 3, 4, 5];
const result = arr.reversedValues().map(x => x + 1).toArray(); // [6,5,4,3,2]
// or
const result = arr.values().reverse().map(x => x + 1).toArray(); // [6,5,4,3,2]

which is much easier to understand imo, using the familiar term "reverse(d)" instead of "right".

1 Like

I agree that the overall complexity remains O(n), but avoiding reversal eliminates an extra pass since both mapping and reversing are individually O(n).

Most importantly, it provides a clear mental model for engineersβ€”if they want to render elements from right to left, they can simply use mapRight.