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) => { ... } )
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 ]
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));
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));
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));
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));
})();
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)