DEV Community

Cover image for Deep dive into Javascript Array.map().
Renato Pozzi
Renato Pozzi

Posted on • Updated on

Deep dive into Javascript Array.map().

So you are starting looking into the array's map function and wanna discover how it works under the hood?

Today for you I have made three examples with an extra bonus example, which will help you understand how a map can be rewritten.

Let's get started!

Map Function Signature

First in first, let's check how the map signature is. From the MDN docs, we can see the following:

// Arrow function
map((element) => { ... } )
map((element, index) => { ... } )
map((element, index, array) => { ... } )
Enter fullscreen mode Exit fullscreen mode

So there is a first element parameter, which is mandatory, and optional index parameter that represents the current element index in the map, and finally, an array parameter that contains all the array that we are mapping.

An Original Example

What we are going to do is to make a map functions of an array of numbers incrementing each value by one, following there is an example of the original map:

const data = [1, 2, 3, 4, 5];

const increment = (el, index, array) => {
  console.log("Current Element:", el, "index:", index, "array:", array);
  return el + 1;
};

console.log("Original Map:", data.map(increment));

// Output
Current Element: 1 index: 0 array: [ 1, 2, 3, 4, 5 ]
Current Element: 2 index: 1 array: [ 1, 2, 3, 4, 5 ]
Current Element: 3 index: 2 array: [ 1, 2, 3, 4, 5 ]
Current Element: 4 index: 3 array: [ 1, 2, 3, 4, 5 ]
Current Element: 5 index: 4 array: [ 1, 2, 3, 4, 5 ]
Original Map: [ 2, 3, 4, 5, 6 ]
Enter fullscreen mode Exit fullscreen mode

Pseudocode Idea

The idea of the map is that for each value x returns an f(x) value, where f() is our mapping function. Let's get started with the alternative one:

Alternative One!

So, for the first alternative, we can make use of the for ... in loop in order to loop all the array elements, and use an increment function to return the incremented value.

const data = [1, 2, 3, 4, 5];

const increment = (el, index, array) => el + 1;

const alternative = (arr, fn) => {
  const mapped = [];
  for (const index in arr) {
    mapped.push(fn(arr[index], index, arr));
  }
  return mapped;
};

console.log("Original Map:", data.map(increment));
console.log("Alternative Map:", alternative(data, increment));
Enter fullscreen mode Exit fullscreen mode

As you can see, we need to pass the data array as an argument to our custom map function, this is fine because extending native objects usually is considered a bad practice in production.

Alternative Two!

If you think that five rows for a map alternative are too much, I have another solution for you, this time using forEach. Let's see it:

const data = [1, 2, 3, 4, 5];

const increment = (el, index, array) => el + 1;

const alternative = (arr, fn) => {
  const mapped = [];
  arr.forEach((el, i) => mapped.push(fn(el, i, arr)));
  return mapped;
};

console.log("Original Map:", data.map(increment));
console.log("Alternative Map:", alternative(data, increment));
Enter fullscreen mode Exit fullscreen mode

In this case, with only three rows we have made a fully operational map alternative! But we can do more again. Maybe with only one row? It seems hard.. we have to reduce the chances ..

Alternative Three!

This is the most badass alternative. Using reduce we can make a magically beautiful map, with only one line of code:

const data = [1, 2, 3, 4, 5];

const increment = (el, index, array) => el + 1;

const alternative = (arr, fn) =>
  arr.reduce((acc, el) => [...acc, fn(el, acc.length, arr)], []);

console.log("Original Map:", data.map(increment));
console.log("Alternative Map:", alternative(data, increment));
Enter fullscreen mode Exit fullscreen mode

As you have seen, there are many possibilities if you want to create your own custom functions, based on the need you can choose the one that works best!

Bonus Alternative: Async Map

Sometimes it happens to have to map the data through the result of an asynchronous function, so how is it possible to implement a custom version of the map that provides this possibility?

const data = [1, 2, 3, 4, 5];

(async () => {
  const increment = async (el, index, array) =>
    new Promise((resolve, reject) => setTimeout(() => resolve(el + 1), 1000));

  const alternative = async (arr, fn) => {
    const mapped = [];
    for (const index in arr) {
      const x = await fn(arr[index], index, arr);
      mapped.push(x);
      console.log("Processed value:", x, "at:", Date.now());
    }
    return mapped;
  };

  console.log("Async Map Ended:", await alternative(data, increment));
})();
Enter fullscreen mode Exit fullscreen mode

This function uses the same mapping function as the others, with the only difference that each element mapping waits a second before proceeding to the next.

Wrapping up

So, today you have discovered how to create a map function completely from scratch, and also how to implement an asynchronous map.

What was the alternative that seemed most useful to you? Let me know with a comment below!

Thank you for reading and I hope I was helpful!

See you on Twitter! @imarenny

Top comments (0)