Implementing JavaScript Array.prototype.reduce function


As reference you can read the specification for Array.prototype.reduce on MDN Docs

At first I thought, the function should follow these requirements:

  • Start at the first value of an array if the initial value is not provided
  • Go through all elements of an array (duh)
  • Apply callback function on the result

Here is my first implementation:

Array.prototype.myReduce = function (callbackFn, initialValue) {
  let res = initialValue ?? this.at(0);
  for (let val of this) {
    res = callbackFn(res, val);
  }
  return res;
};

Did you spot a bug? Yes, I forgot to note that the initial index will change in case if we use element at index 0 as initial value. I quickly fixed that

Array.prototype.myReduce = function (callbackFn, initialValue) {
  let res = initialValue ?? this.at(0);
  let initialIdx = initialValue !== undefined ? 0 : 1;
 
  for (let i = initialIdx; i < this.length; i++) {
    res = callbackFn(res, this[i], i, this);
  }
  return res;
};

While it covers all requirements I have listed, it fails in case of using the third and fourth argument of the callback function: the index of the element in array, and the array that the function is called on. Hence, another requirement should be added to the list:

  • Handle all parameters of the callback function

And the function now looks like this:

Array.prototype.myReduce = function (callbackFn, initialValue) {
  let res = initialValue ?? this.at(0);
  let initialIdx = initialValue !== undefined ? 0 : 1;
 
  for (let i = initialIdx; i < this.length; i++) {
    res = callbackFn(res, this[i], i, this);
  }
  return res;
};

Index and array arguments may be useful for some kinds of computation:

const scores = [80, 90, 100];
 
const weightedAvg = scores.reduce((acc, val, idx, arr) => {
  const weight = idx + 1;
  const totalWeight = (arr.length * (arr.length + 1)) / 2;
  return acc + (val * weight) / totalWeight;
}, 0);
 
console.log("Weighted Average:", weightedAvg);

As a bonus, second implementation covers another requirement from documentation:

  • Handle being called on object values
  • Handle case when initial value is provided and array is empty

However it still fails in several cases:

  • When no initial value is provided and array is empty, return TypeError
  • When no initial value is provided and array has length equal to 1, return 1st element

To account for this, I put two conditions in the very beginning

Array.prototype.myReduce = function (callbackFn, initialValue) {
  if (this.length === 0 && initialValue === undefined) {
    throw TypeError;
  }
  if (this.length === 1 && initialValue === undefined) {
    return this.at(0);
  }
 
  let res = initialValue ?? this.at(0);
  let initialIdx = initialValue !== undefined ? 0 : 1;
 
  for (let i = initialIdx; i < this.length; i++) {
    res = callbackFn(res, this[i], i, this);
  }
  return res;
};

Looks like we finished? Not really. Not only it needs some refactoring to follow DRY (Don’t Repeat Yourself) principle, it also doesn’t account for the sparse arrays. (sparse array contains an empty element, an example would be [1,2,,,3]). Adding a check if (i in this) will skip any empty element.

Here is the final function implementation:

Array.prototype.myReduce = function (callbackFn, initialValue) {
  let res;
  let initialIdx = 0;
 
  if (initialValue === undefined) {
    if (this.length === 0) {
      throw TypeError;
    }
    if (this.length === 1) {
      return this.at(0);
    }
    res = this.at(0);
    initialIdx += 1;
  } else {
    res = initialValue;
  }
 
  for (let i = initialIdx; i < this.length; i++) {
    if (i in this) {
      res = callbackFn(res, this[i], i, this);
    }
  }
 
  return res;
};

Here is the list of properties that it satisfies:

✅ Start at the first value of an array if the initial value is not provided

✅ Go through all elements of an array

✅ Apply callback function on the result

✅ Handle all parameters of the callback function

✅ Handle being called on object values

✅ Handle case when initial value is provided and array is empty

✅ When no initial value is provided and array is empty, return TypeError

✅ When no initial value is provided and array has length equal to 1, return 1st element


I hope this deep dive into implementing the reduce() function has given you a better understanding of its inner workings and challenges.