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 || functionreduce(callbackfn) {
    "use strict";
    // 1.if (this === undefined || this === null) {
      thrownewTypeError("Array.prototype.reduce called on null or undefined");
    }
    let O = Object(this);
    // 2.let len = ToLength(O.length);
    // 3.if (typeof callbackfn != 'function') {
      thrownewTypeError(`${String(callbackfn)} is not a function`);
    }
    // 4.if (len == 0 && arguments.length < 2) {
      thrownewTypeError("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.letPk = String(k);
        // ii.
        kPresent = Pkin O;
        // iii.if (kPresent) accumulator = O[Pk]; // 1.// iv.
        k++;
      }
      // c.if (!kPresent) thrownewTypeError("Reduce of empty array with no initial value");
    }
    // 8.while (k < len) {
      // a.letPk = String(k);
      // b.let kPresent = Pkin O;
      // c.if (kPresent) {
        // i.let kValue = O[Pk];
        // ii.
        accumulator = callbackfn(accumulator, kValue, k, O);
      }
      // d.
      k++;
    }
    // 9.return accumulator;
  }
});

functionToInteger(argument) {
  letnumber = Number(argument);

  if (isNaN(number)) return0;

  switch (number) {
  case0:
  caseInfinity:
  case -Infinity:
    returnnumber;
  }

  returnparseInt(number);
}

functionToLength(argument) {
  let len = ToInteger(argument);

  if (len <= 0) return0;
  if (len == Infinity) returnNumber.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"