Skip to main content

Command Palette

Search for a command to run...

Map, Filter, and Reduce in JavaScript (Explained with Polyfills)

Published
7 min read
S

Hi, I am a web developer, always curious and exploring new tech

Map, filter, and reduce were introduced in ES6 (ECMAScript 2015) as modern ways to iterate over arrays.
Each of these methods serves a different purpose, allowing us to write cleaner, more readable, and functional JavaScript code.
Let’s understand how each of them works one by one.

map()

map() is an array method that takes a callback function and executes that callback once for each element in the array.
It returns a new array with the transformed values.

The callback function receives three parameters:

  1. item – current element

  2. index – index of the current element

  3. array – the original array

Syntax

array.map((item, index, array) => {
  // return new value
});

Example

let arr = [1, 2, 3, 4, 5];

function multiplyBy2(num, index, arr) {
  return num * 2;
}

let result = arr.map(multiplyBy2);
console.log(result); // [2, 4, 6, 8, 10]

Using Arrow Function

let result = arr.map((item, index, array) => {
  return item * 2;
});

Key Points

  • map() does not modify the original array

  • Always returns a new array

  • Best used for transforming data

filter()

filter() is similar to map(), but instead of transforming values, it selects elements based on a condition.

It takes a callback function and executes it for every element in the array.
The callback function receives three arguments:

  1. item – current element

  2. index – index of the current element

  3. array – the original array

The callback must return:

  • true → element is included in the new array

  • false → element is excluded

filter() always returns a new array containing only the elements that satisfy the condition.


Example

function isEvenNumber(num, index, arr) {
  if (num % 2 === 0) {
    return true;
  }
  return false;
}

let arr = [1,2,3,4,5,6,7,8,9,10];

let result = arr.filter(isEvenNumber);
console.log(result); // [2, 4, 6, 8, 10]

Using Arrow Function

let result = arr.filter((num, index, arr) => num % 2 === 0);

Key Points

  • filter() does not modify the original array

  • Returns a new array

  • Output array length may be less than or equal to the original

  • Best used for removing unwanted elements

reduce()

reduce() is an array method introduced in ES6 (ECMAScript 2015).
It iterates over all elements of an array and reduces them to a single value.

It is mainly used for aggregation operations, such as:

  • Calculating the sum of elements

  • Finding the product

  • Building a single result from multiple values

It can also be used for advanced operations like filtering, grouping, and transforming data into objects.


How reduce() Works

reduce() takes two arguments:

  1. A callback function

  2. An optional initial value

The callback function receives four parameters:

  1. accumulator – stores the accumulated result

  2. current – current element

  3. index – index of the current element

  4. array – original array

📌 If the initial value is not provided, the first element of the array becomes the initial accumulator.


Example: Sum of All Elements

function addNumbers(acc, curr, index, arr) {
  return acc + curr;
}

let arr = [1,2,3,4,5,6,7,8,9,10];

let result = arr.reduce(addNumbers);
console.log(result); // 55

Using Arrow Function with Initial Value

let result = arr.reduce((acc, curr, index, arr) => {
  return acc + curr;
}, 0);

console.log(result); // 55

Key Points

  • Introduced in ES6 (ECMAScript 2015)

  • Returns a single value

  • Does not mutate the original array

  • Extremely powerful and flexible

Polyfills for map, filter, and reduce

Before writing polyfills, let’s understand why we need them.

Although modern JavaScript array methods work perfectly, some older browsers do not support ES6 (ECMAScript 2015) features.
To ensure our application works across all environments, we create polyfills—custom implementations that mimic native methods when they are not available.

Polyfills help prevent our application from breaking in unsupported environments.


Polyfill for map()

To make our polyfill behave like the original map() method, we attach it to the Array prototype.

Rules our polyfill should follow

  • Must work on arrays

  • Must accept a callback function

  • Must return a new array

  • Must not modify the original array


map() Polyfill


Array.prototype.myMap = function (callback) {
  if (this == null) {
    throw new TypeError("Cannot read property 'map' of null or undefined");
  }

  let result = [];

  for (let i = 0; i < this.length; i++) {
    result.push(callback(this[i], i, this));
  }

  return result;
};

Usage

let arr = [1, 2, 3, 4];

let output = arr.myMap((num) => num * 2);

console.log(output); // [2, 4, 6, 8]

Let’s also make the polyfill handle sparse arrays, where values are not present at some indices.

In such cases, map() does not call the callback function for those missing indices.
We should keep this behavior in mind while writing the polyfill so that it works exactly like the native map() method.

Array.prototype.myMap = function (callback) {
    if (this === null) {
        throw new TypeError("can not read properties of null or undefined")
    }
    let arr = []
    for (let i = 0; i < this.length; i++) {
        if (i in this) {
            arr.push(callback(this[i], i, this))
        }
    }
    return arr
}

filter()

Let’s write a polyfill for the filter() method.

We are going to use Array.prototype this method, which is attached to the array prototype.
After that, we can call this method directly on any array.

We will take a callback function as an argument.
Inside the function, we will first throw a TypeError if this is null or undefined.

Then, we will create a new array to store the filtered values.

Next, we will loop over the array using a normal loop.
If an index is not empty and the callback function returns true, we will push that element into the new array.

After the loop ends, we will return the new array.

Array.prototype.myFilter = function (callback) {
    if (this == null) {
        throw new TypeError("can not read properties of null or undefined")
    }
    let newarr = []
    for (let i = 0; i < this.length; i++) {
        if (i in this && callback(this[i], i, this)) {
            newarr.push(this[i])
        }
    }
    return newarr
}

//usage of polyfill 
let filterresult = arr.myFilter((item) => item % 2 == 0)
console.log(filterresult)

reduce()

Let’s write a polyfill for the reduce() method.

First, we need to handle some edge cases and check that the array is not null or undefined.

Next, we declare a variable called accumulator and assign it the initial value (if provided).

We also define a boolean flag called isInitialized to check whether the user has passed an initial value.
If the number of arguments is less than 2, it means the user has not passed an initial value. We need this check to decide how the accumulator should be initialized.

Now, we start a loop from 0 to the length of the array. Inside the loop:

  • First, we check if a value is present at the current index (to handle sparse arrays).

  • If the user has passed an initial value, we call the callback function and reassign the returned value to the accumulator.

  • If the user has not passed an initial value, we assign the first available element of the array to the accumulator, set isInitialized to true, and continue the loop.

This loop runs until the end of the array.

After the loop ends, we check whether isInitialized is still false.
If it is, that means the array was empty and no initial value was provided, so we throw an error:

“reduce of empty array with no initial value”

Finally, we return the accumulator.

Array.prototype.myReduce = function (callback, inititalValue) {
    if (this == null || this == undefined) {
        throw new TypeError("can not read properties of null and undefined")
    }
    let accumulator = inititalValue
    // Check if the user actually passed a second argument (even if it was undefined)
    let isInitialized = arguments.length >= 2;

    for (let i = 0; i < this.length; i++) {
        if (i in this) {
            if (isInitialized) {
                accumulator = callback(accumulator, this[i], i, this)
            } else {
                accumulator = this[i]
                isInitialized = true
            }
        }
    }

    //array was empty and no initial value was provided
    if (!isInitialized) {
        throw new TypeError("Reduce of empty array with no initial value");
    }

    return accumulator
}

let arr = [1,2,3,4,5,6,7,8,9,10]
let result arr.myReduce((acc,curr)=>acc+curr,0)
console.log(result)