Skip to content Skip to sidebar Skip to footer

What Are The Consequences Of Mutating The Array While Applying Array.reduce To It

Suppose I have an array: const ar = [1,2,3,4]; And I apply reduce function to it and inside that function I remove elements like this: ar.reduce((result, element, index, original)

Solution 1:

In your second example, it is in fact executed for the 1st and 3rd elements, not for the first two:

const ar = [1, 2, 3, 4];

ar.reduce((result, element, index, original)=>{
    console.log(element, index);
    original.splice(index, 1);
}, []);

console.log(ar);
1 2 3 4
^

Here, while reduce's element is 1 and index is 0, it calls splice, removing the first element, then iterates to the next index:

2 3 4
  ^

Here, reduce's element is 3 and index is 1. After removing that, index will be equal to ar.length and it stops, leaving you with

2 4

The reason reduceRight() will still visit all the elements is because you iterate backwards, and the previous element positions are not affected by splicing the element at the current index:

const ar = [1, 2, 3, 4];

ar.reduceRight((result, element, index, original)=>{
    console.log(element, index);
    original.splice(index, 1);
}, []);

console.log(ar);

And the walkthrough:

element = 4, index = 3

1 2 3 4
      ^

element = 3, index = 2

1 2 3
    ^

element = 2, index = 1

1 2
  ^

element = 1, index = 0

1
^

To answer your question, yes ECMAScript documents this behavior for Array#reduce() as part of the specification:

The range of elements processed by reduce is set before the first call to callbackfn. Elements that are appended to the array after the call to reduce begins will not be visited by callbackfn. If existing elements of the array are changed, their value as passed to callbackfn will be the value at the time reduce visits them; elements that are deleted after the call to reduce begins and before being visited are not visited.

And the exact same paragraph as above applies to reduceRight as well.

Below is a polyfill for Array#reduce(), following the steps from the specification:

Object.defineProperty(Array.prototype, 'reduce', {
  configurable: true,
  writable: true,
  value: Array.prototype.reduce || function reduce(callbackfn) {
    "use strict";
    // 1.
    if (this === undefined || this === null) {
      throw new TypeError("Array.prototype.reduce called on null or undefined");
    }
    let O = Object(this);
    // 2.
    let len = ToLength(O.length);
    // 3.
    if (typeof callbackfn != 'function') {
      throw new TypeError(`${String(callbackfn)} is not a function`);
    }
    // 4.
    if (len == 0 && arguments.length < 2) {
      throw new TypeError("Reduce of empty array with no initial value");
    }
    // 5.
    let k = 0;

    let accumulator;
    // 6.
    if (arguments.length >= 2) {
      // a.
      accumulator = arguments[1];
    // 7.
    } else {
      // a.
      let kPresent = false;
      // b.
      while (!kPresent && k < len) {
        // i.
        let Pk = String(k);
        // ii.
        kPresent = Pk in O;
        // iii.
        if (kPresent) accumulator = O[Pk]; // 1.
        // iv.
        k++;
      }
      // c.
      if (!kPresent) throw new TypeError("Reduce of empty array with no initial value");
    }
    // 8.
    while (k < len) {
      // a.
      let Pk = String(k);
      // b.
      let kPresent = Pk in O;
      // c.
      if (kPresent) {
        // i.
        let kValue = O[Pk];
        // ii.
        accumulator = callbackfn(accumulator, kValue, k, O);
      }
      // d.
      k++;
    }
    // 9.
    return accumulator;
  }
});

function ToInteger(argument) {
  let number = Number(argument);

  if (isNaN(number)) return 0;

  switch (number) {
  case 0:
  case Infinity:
  case -Infinity:
    return number;
  }

  return parseInt(number);
}

function ToLength(argument) {
  let len = ToInteger(argument);

  if (len <= 0) return 0;
  if (len == Infinity) return Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1;

  return len;
}

Post a Comment for "What Are The Consequences Of Mutating The Array While Applying Array.reduce To It"