What Are The Consequences Of Mutating The Array While Applying Array.reduce To It
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
reduceis set before the first call tocallbackfn. Elements that are appended to the array after the call toreducebegins will not be visited bycallbackfn. If existing elements of the array are changed, their value as passed tocallbackfnwill be the value at the time reduce visits them; elements that are deleted after the call toreducebegins 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"