Learn JS Array Methods Using Polyfills
If you are confused about when to use which method, this blog is for you.
Table of contents
Writing polyfills makes you understand things more deeply. You will know what's going wrong and what isn't. I will discuss the mainly used polyfills. Through which it will be easier for you to learn and practice it.
Map
The map is a method that iterates over the list, and you can modify each item individually. It returns a new list with an updated reference and not having the Array In-place Mutation. The In-place Mutation is when an original array gets updated with new values in the same memory reference. An example of In-place mutation is Array.reverse(), Array.sort(), etc.
Working Of Map:
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
The map method takes a callback function
as the argument. The callback function is getting called for each item. In other words, It's looping over each item and pushing the return value
of the callback function to an empty array.
Map Rough Implementation
const array1 = [1, 4, 9, 16];
function myMap(array, callback) {
let newArray = [];
for (let i = 0; i < array.length; i++) {
newArray.push(callback(array[i]));
}
return newArray;
}
const map1 = myMap(array1 ,x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
The problem with the current definition of myMap is I have taken the array as a parameter, and there is no option for index value right now.
However, the OG map callback function
has three parameters: item, index, and array. You can read more about it here.
OG Map Working
const map2 = [2, 8, 18, 32].map((item,index,array) => {
console.log(index, ',', array);
return item * 2
})
console.log(map2)
// expected output:
0 , [2,8,18,32]
1 , [2,8,18,32]
2 , [2,8,18,32]
3 , [2,8,18,32]
[4, 16, 36, 64]
Let's implement our map method.
Map Polyfill
// Polyfill for the map
Object.defineProperty(Array.prototype, "myMap", {
value: function (callback) {
let newArray = [];
let i = 0;
while (i < this.length) {
newArray.push(callback(this[i], i, this));
i++;
}
return newArray;
}
});
// TESTS
const map2 = [2, 8, 18, 32].myMap((item,index,array) => {
console.log(index, ',' , array);
return item * 2
})
console.log(map2)
// expected output:
0 , [2,8,18,32]
1 , [2,8,18,32]
2 , [2,8,18,32]
3 , [2,8,18,32]
[4, 16, 36, 64]
Going Through The Code
We are using the Object.defineProperty
to create our version of the map. It will behave as the OG map and for getting the array from the Array.myMap()
. We have used the this
Object. We have called the callback(this[i], i, this)
function with three arguments: this[i](item), i(index), this(array)
.
To know how the Object.defineProperty
works and what it is, read it here.
Filter
The filter is a method that iterates over the list and gives us the ability to return the item which is True
to the callback function
. It returns a new list of filtered items.
Working of filter:
const words = [
"spray",
"limit",
"elite",
"exuberant",
"destruction",
"present"
];
const result = words.filter((word) => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
The filter method takes a callback function
as the argument. The callback function
is getting called for each item. Generally, It's looping over each item and pushing only those item that returns true
in the callback function
.
Filter Rough Implementation
function myfilter(array, callback) {
let newArray = [];
for (let element of array) {
if (callback(element)) {
newArray.push(element);
}
}
return newArray;
}
const result = myFilter(words, (word) => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
The problem with the current filter implementation is the same as the Map Rough Implementation
Filter Polyfill
// Polyfill for the filter
Object.defineProperty(Array.prototype, "myFilter", {
value: function (callback) {
let newArray = [];
let i = 0;
while (i < this.length) {
if (callback(this[i], i, this)) {
newArray.push(this[i]);
}
i++;
}
return newArray;
}
});
// TESTS
const result2 = words.myFilter((word) => word.length > 6);
console.log(result2);
// expected output: Array ["exuberant", "destruction", "present"]
Going Through The Code
We are using the Object.defineProperty
to create our version of the filter. It will behave as the OG filter and for getting the array from the Array.myFilter()
. We have used the this
Object. We have called the callback(this[i], i, this)
function with three arguments: this[i](item), i(index), this(array)
.
To know how the Object.defineProperty
works and what it is, read it here.
Reduce
The reduce is a method that iterates over the list. It gives you the ability to accumulate all items into a single value. It returns a single value that results from reduction.
Working Of Reduce:
Example 1:
let a = [10, 21, 13, 56];
function add(a, b) {
return a + b;
}
const result = a.reduce(add)
console.log(result);
// expected output: 100
Iteration 1 : add(10, 21) => 31
Iteration 2 : add(31, 13) => 44
Iteration 3 : add(44, 56) => 100
Reduce method takes a callback function as the argument. It is getting called for each item in a list and contains two parameters, the accumulator and the current value
. The accumulator has the result value, while the current value
has each item value.
In the above example, we are not providing the initial value
to the accumulator, But we can do that, see the below example.
Example 2:
let a = [10, 21, 13, 56];
function add(a, b) {
return a + b;
}
const result = a.reduce(add,10) // 2nd Argument of reduce method is for initial value
console.log(result);
// expected output: 110
Iteration 1 : add(10, 10) => 20
Iteration 2 : add(20, 21) => 41
Iteration 3 : add(41, 13) => 54
Iteration 4 : add(54, 56) => 110
In example two, We have provided the initial value
to the accumulator, such that the current value
starts from the index 0
.
In example one, we haven't provided an initial value
to the accumulator. So accumulator took the initial value as index zero
, while the current value
got the index one
value.
Reduce Rough Implementation
let b = [10, 21, 13, 56];
function add(a, b) {
return a + b;
}
function foo(a, b) {
return a.concat(b);
}
function myReduce(array, callback, initial) {
let i = 0;
let acc = initial;
if (arguments.length < 3) {
i = 1;
acc = array[0];
}
while (i < array.length) {
acc = callback(acc, array[i]);
i++;
}
return acc;
}
console.log(myReduce(b, add), b.reduce(add)); // 100 100
console.log(myReduce(b, add, 10), b.reduce(add, 10)); // 110 110
console.log(myReduce(b, foo, "X"), b.reduce(foo, "X")); // X10211356 X10211356
I am checking the condition arguments.length< 3
, to know if have the initial value. If I don't have the initial value, The current value
gets index one and the accumulator
to array[0]
.
If I have the initial value, it won't execute the if condition and takes the default values of the accumulator, which is the initial
and current value
has an index zero
.
The problem with the current implementation is the same as Map Rough Implementation.
Reduce Polyfill
// Polyfill for the reduce
Object.defineProperty(Array.prototype, "myReduce", {
value: function (callback, initial) {
let i = 0;
let acc = initial;
if (arguments.length < 2) {
i = 1;
acc = this[0];
}
while (i < this.length) {
acc = callback(acc, this[i], i, this);
i++;
}
return acc;
}
});
// Tests
let a = [10, 21, 13, 56];
function add(a, b) {
return a + b;
}
function foo(a, b) {
return a.concat(b);
}
console.log(a.myReduce(add), a.reduce(add)); // 100 100
console.log(a.myReduce(add, 10), a.reduce(add, 10)); // 110 110
console.log(a.myReduce(foo, "X"), a.reduce(foo, "X")); // X10211356 X10211356
Going Through The Code
We are using the Object.defineProperty
to create our version of Reduce. It will behave as the OG reduce and for getting the Array from Array.myFilter(). We have used the this
Object. We have called the callback(this[i], i, this)
function with four arguments: acc, this[i](item), i(index), and this(array)
.
To know how the Object.defineProperty
works and what it is, read it here.
Things To Keep In Mind
Though these implementations of polyfills will work fine, one should consider the edge cases for professional use. You can refer to that from the official MDN docs. They have well-written and tested polyfills, while the logic behind the implementation is still the same.
Connect with me on Twitter, and LinkedIn.