Deep dive into Javascript Array.map().
Published: 2021-05-20 14:37:32

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:

```javascript
// Arrow function
map((element) => { ... } )
map((element, index) => { ... } )
map((element, index, array) => { ... } )
```

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:

```javascript
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 ]
```

## 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.

```javascript
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));
```

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:

```javascript
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));
```

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:

```javascript
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));
```

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?

```javascript
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));
})();
```

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](https://twitter.com/imarenny)