On this page
Polyfill Reduce
Last reviewed 14 Sept 2026
A polyfill for reduce is another common interview question. First, recall how the real reduce works:
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((accumulator, currentValue) => { return accumulator + currentValue;}, 0);
console.log(sum); // 10reduce(callback, initialValue) calls callback(accumulator, currentValue, currentIndex, array) for each element and returns the final accumulator.
- If
initialValueis given → the accumulator starts with it, and the loop starts at index 0. - If not → the accumulator starts with the first element, and the loop starts at index 1.
- Empty array with no
initialValue→TypeError.
myReduce polyfill
Array.prototype.myReduce = function (callback, initialValue) { const originalArray = this;
// Check if 'this' is an array and callback is a function if (!Array.isArray(originalArray)) { throw new TypeError("myReduce called on non-array"); } if (typeof callback !== "function") { throw new TypeError(callback + " is not a function"); }
// Initialize accumulator let acc; let startIndex;
if (arguments.length >= 2) { acc = initialValue; startIndex = 0; // start from the first element } else { if (originalArray.length === 0) { throw new TypeError("Reduce of empty array with no initial value"); } acc = originalArray[0]; startIndex = 1; // skip the first element }
// Iterate over the array for (let i = startIndex; i < originalArray.length; i++) { acc = callback(acc, originalArray[i], i, originalArray); }
return acc;};
const sum1 = numbers.myReduce((accumulator, currentValue) => { return accumulator + currentValue;}, 0);
console.log(sum1); // 10console.log([1, 2, 3].myReduce((a, b) => a * b)); // 6 — no initial value// [].myReduce((a, b) => a + b); // TypeError: Reduce of empty array with no initial valueWhy arguments.length >= 2 instead of initialValue !== undefined? Someone may intentionally pass undefined as the initial value. The real reduce treats reduce(fn, undefined) as “initial value given”, so checking the number of arguments matches it exactly.
console.log([1, 2].reduce((acc, n) => String(acc) + n, undefined)); // "undefined12"console.log([1, 2].myReduce((acc, n) => String(acc) + n, undefined)); // "undefined12"Check the first duplicate using reduce
const nums = [2, 5, 3, 5, 6, 3, 2];
const firstDuplicate = nums.reduce((acc, currentValue, currentIndex, array) => { if (acc !== null) return acc; // if a duplicate is already found, skip further checks
// if this value appeared earlier in the array, it is a duplicate const isDuplicate = array.indexOf(currentValue) !== currentIndex; return isDuplicate ? currentValue : acc;}, null);
console.log(firstDuplicate); // 5How: at index 3, the value is 5, but indexOf(5) is 1 (its first position), so 1 !== 3 → duplicate. 5 is the first value whose second occurrence appears, so the answer is 5.
A faster version for large arrays uses a Set (indexOf makes the reduce version O(n²)):
function findFirstDuplicate(arr) { const seen = new Set(); for (const n of arr) { if (seen.has(n)) return n; seen.add(n); } return null;}console.log(findFirstDuplicate([2, 5, 3, 5, 6, 3, 2])); // 5Understand reduce step by step
const values = [10, 20, 30, 40];
const sumWithDebug = values.reduce((acc, currentValue, currentIndex, array) => { console.log(`Index: ${currentIndex}, Value: ${currentValue}, Accumulator: ${acc}, Array: ${array}`); return acc + currentValue;}, 0);
console.log(sumWithDebug);Output:
Index: 0, Value: 10, Accumulator: 0, Array: 10,20,30,40Index: 1, Value: 20, Accumulator: 10, Array: 10,20,30,40Index: 2, Value: 30, Accumulator: 30, Array: 10,20,30,40Index: 3, Value: 40, Accumulator: 60, Array: 10,20,30,40100Bonus: polyfills for map and filter
Array.prototype.myMap = function (callback) { const result = []; for (let i = 0; i < this.length; i++) { result.push(callback(this[i], i, this)); } return result;};
Array.prototype.myFilter = function (callback) { const result = []; for (let i = 0; i < this.length; i++) { if (callback(this[i], i, this)) result.push(this[i]); } return result;};
console.log([1, 2, 3].myMap(n => n * 2)); // [2, 4, 6]console.log([1, 2, 3, 4].myFilter(n => n % 2 === 0)); // [2, 4]Useful reduce patterns
// Count occurrencesconst fruits = ['apple', 'banana', 'apple'];console.log(fruits.reduce((acc, f) => ({ ...acc, [f]: (acc[f] || 0) + 1 }), {}));// { apple: 2, banana: 1 }
// Flatten one levelconsole.log([[1, 2], [3], [4, 5]].reduce((acc, arr) => acc.concat(arr), []));// [1, 2, 3, 4, 5]
// Max valueconsole.log([3, 9, 2].reduce((max, n) => (n > max ? n : max)));// 9
// Run functions in sequence (pipe)const pipe = (...fns) => x => fns.reduce((v, fn) => fn(v), x);console.log(pipe(n => n + 1, n => n * 2)(5));// 12Interview practice: Interview version with sparse arrays,
thisArgand the empty-array rule: Machine Coding — map, filter and reduce.