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
reduce
is set before the first call tocallbackfn
. Elements that are appended to the array after the call toreduce
begins will not be visited bycallbackfn
. If existing elements of the array are changed, their value as passed tocallbackfn
will be the value at the time reduce visits them; elements that are deleted after the call toreduce
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"