Map, Filter, and Reduce in JavaScript (Explained with Polyfills)
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:
item– current elementindex– index of the current elementarray– 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 arrayAlways 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:
item– current elementindex– index of the current elementarray– the original array
The callback must return:
true→ element is included in the new arrayfalse→ 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 arrayReturns 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:
A callback function
An optional initial value
The callback function receives four parameters:
accumulator– stores the accumulated resultcurrent– current elementindex– index of the current elementarray– 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
isInitializedtotrue, 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)

