Learn JS Array Methods Using Polyfills

Learn JS Array Methods Using Polyfills

If you are confused about when to use which method, this blog is for you.

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.