- Implement a
map()
function from scratch - Explain how
map()
differs fromreduce()
- Explain how
map()
builds onreduce()
In a lot of code you'll be writing as a soon-to-be JS expert, you'll be iterating over arrays. Sometimes, you'll want to
apply a transformation to the elements in the array. We can do this using for
loops, but writing all of that stuff
gets tedious after a while. Let's create our own helper method called map()
to make things a little easier!
To start things off, let's write a map()
function that abstracts away the for
loop:
function map(collection) {
for (let i = 0; i < collection.length; i++) {
const element = collection[i];
console.log(element);
}
}
This will log all of the elements in the array. Not terribly interesting. Let's add a second argument so we can pass a
function to our map()
function. This function will receive the element
and can then optionally transform it. We'll
also need a new array to store our results in, so we can return the result when we're done:
function map(collection, callback) {
const result = [];
for (let i = 0; i < collection.length; i++) {
const element = collection[i];
result.push(callback(element));
}
return result;
}
Sweet! That should work for now. Let's take this baby for a spin by doubling a list of numbers:
const numbers = [1, 2, 3];
const doubledNumbers = map(numbers, function (number) {
return number * 2;
});
console.log(doubledNumbers); // prints [2, 4, 6]
In case we ever need the index of the item or the full list of items in our callback function, let's add these as arguments to our callback. The callback doesn't have to use these values, but they're there if we ever need them.
function map(collection, callback) {
const result = [];
for (let i = 0; i < collection.length; i++) {
const element = collection[i];
result.push(callback(element, i, collection));
}
return result;
}
Let's use our map()
function on a trickier data structure — a list of Autobots. To start things off, we have an array
of Autobots. Now, let's transform all of them to their robotic form! A transformed Autobot needs to be marked as such
using the isTransformed
boolean, as well as have its strength doubled:
const autobots = [
{ name: 'Optimus Prime', strength: 5, isTransformed: false, },
{ name: 'Ironhide', strength: 3, isTransformed: false, },
{ name: 'Bumblebee', strength: 2.5, isTransformed: false, },
{ name: 'Ratchet', strength: 1.5, isTransformed: false, },
];
const transformedAutobots = map(autobots, function (autobot) {
return Object.assign({}, autobot, {
strength: autobot.strength * 2,
isTransformed: true,
});
});
console.log(transformedAutobots);
/*
Result:
[
{ name: 'Optimus Prime', strength: 10, isTransformed: true },
{ name: 'Ironhide', strength: 6, isTransformed: true },
{ name: 'Bumblebee', strength: 5, isTransformed: true },
{ name: 'Ratchet', strength: 3, isTransformed: true }
]
*/
We're using Object.assign()
here to defensively copy the object and change its values. If we didn't, the objects in
the original array would get modified too. Defensive copying is important to keep in mind — modifying values all over
our code is often the biggest source of bugs.
Time for a confession. We basically just implemented something that is already part of the JS standard library. Sisyphus
has nothing on us! To map elements in an array, we can simply use Array.prototype.map()
.
Much like our own map()
function, Array.prototype.map()
is an array method that iterates over all elements, allowing
you to apply a function to each element in that array, effectively transforming them into something else. The result is then
returned as a new array, leaving the original array intact and unmodified (but remember, not the elements we modify,
necessitating the need for defensive copying). That last part is super important, because it either saves us from having
to create a new array ourselves and copy stuff in there, or modifying the original elements in the array — much like
what we did in our own map()
function.
Just so you believe I'm not pulling your leg, let's see what it looks like:
const transformedAutobotsWithMap = autobots.map(function (autobot) {
return Object.assign({}, autobot, {
strength: autobot.strength * 2,
isTransformed: true,
});
});
console.log(transformedAutobotsWithMap);
In this code snippet, we're using the native .map()
function that is a property of Array
's prototype. It gives us
the exact same result! Now that we know how map is implemented, it holds no more secrets for us! We can discard our own
map()
function and just use the .map()
property on arrays. Sweet!