Tech Blog
JavaScriptTypeScriptPolyfillsFrontend

Writing a Polyfill of the Array reduce() Method in JavaScript

May 8, 20234 min read
Writing a Polyfill of the Array reduce() Method in JavaScript

Writing polyfills is one of the best ways to understand how JavaScript built-ins really work. Today we'll implement Array.prototype.reduce from scratch—edge cases and all.

How reduce() Works

reduce() iterates through an array, calling a callback function on each element and passing the result (the accumulator) forward to the next call. The final accumulated value is returned.

const nums = [1, 2, 3, 4];
const sum = nums.reduce((acc, cur) => acc + cur, 0);
// => 10

The callback receives four arguments:

  • accumulator — the running result
  • currentValue — the current element
  • currentIndex — the index of the current element
  • array — the original array

The Polyfill

Array.prototype.myReduce = function (callback, initialValue) {
  // 'this' refers to the array the method is called on
  const array = this;
  let accumulator;
  let startIndex;

  if (arguments.length >= 2) {
    // Initial value was provided
    accumulator = initialValue;
    startIndex = 0;
  } else {
    // No initial value — use first element, skip it in iteration
    if (array.length === 0) {
      throw new TypeError(
        "Reduce of empty array with no initial value"
      );
    }
    accumulator = array[0];
    startIndex = 1;
  }

  for (let i = startIndex; i < array.length; i++) {
    accumulator = callback(accumulator, array[i], i, array);
  }

  return accumulator;
};

Edge Cases Handled

Empty array without an initial value

The native reduce() throws a TypeError in this case. Our polyfill mirrors that behaviour:

[].myReduce((acc, cur) => acc + cur);
// TypeError: Reduce of empty array with no initial value

Single element, no initial value

The element itself is returned without calling the callback at all:

[42].myReduce((acc, cur) => acc + cur);
// => 42

Practical Uses

Sum

[1, 2, 3].myReduce((acc, n) => acc + n, 0); // 6

Flatten one level

[[1, 2], [3, 4]].myReduce((acc, arr) => acc.concat(arr), []);
// [1, 2, 3, 4]

Count occurrences

["a", "b", "a", "c", "b", "a"].myReduce((acc, letter) => {
  acc[letter] = (acc[letter] ?? 0) + 1;
  return acc;
}, {});
// { a: 3, b: 2, c: 1 }

Group by property

const people = [
  { name: "Alice", dept: "Engineering" },
  { name: "Bob", dept: "Design" },
  { name: "Carol", dept: "Engineering" },
];

people.myReduce((acc, person) => {
  (acc[person.dept] = acc[person.dept] ?? []).push(person.name);
  return acc;
}, {});
// { Engineering: ["Alice", "Carol"], Design: ["Bob"] }

Understanding reduce() at this level makes every other higher-order array method easier to reason about. Once you can write the polyfill, you can implement map, filter, and find on top of it—they're all just reduce in disguise.