I've been posting daily JavaScript challenges on Twitter lately, and today's challenge involves re-implementing Array.map.
This is certainly not the only solution for this, but here's my stab at it.
What does Array.map do?
Full details can be found on the MDN docs, but here's the gist of things:
- .map lives on the Array prototype
- It returns a new Array from after calling a callback function on every element of the array on which you call it.
- The callback function has 3 arguments, the last two of which are optional: the current item, the current index, and the original array you called .map on.
- You can optionally pass in a second argument after the callback, which will be the
this
context to call the callback with. If you don't pass in athis
context, the callback will be called withundefined
as thethis
context.
Example of Array.map
const exercises = [
"Bench Press",
"Svend Press",
"Chest Flyes",
"Kettlebell Swing",
"40 Yard Sprint",
];
const arr = exercises.map(item => {
return "mapped " + item";
})
// returns:
[ 'mapped Bench Press',
'mapped Svend Press',
'mapped Chest Flyes',
'mapped Kettlebell Swing',
'mapped 40 Yard Sprint' ]
Re-implementing the method
1. Set up our function with the arguments we know it will take.
We know map accepts a callback
and a this
context. We also know that the this
context will be set to undefined
if one is not passed in, so we'll set a default value on that argument.
function newMap(callback, thisArg = undefined) {
// more code will go here
}
2. Exit early if there's a problem and give error messages.
First check is to make sure newMap
was called on an array.
function newMap(callback, thisArg = undefined) {
// Ensure it called on an array.
if (!Array.isArray(this)) {
throw new TypeError('Must be called on an array');
}
}
Next, make sure a callback was supplied since it is required.
function newMap(callback, thisArg = undefined) {
// Ensure it called on an array.
if (!Array.isArray(this)) {
throw new TypeError('Must be called on an array');
}
// Make sure a callback was supplied.
if ( !callback ) {
throw new TypeError('undefined is not a function');
}
}
Lastly, make sure that if a callback was supplied, it is a function.
function newMap(callback, thisArg = undefined) {
// Ensure it called on an array.
if (!Array.isArray(this)) {
throw new TypeError('Must be called on an array');
}
// Ensure a callback was supplied.
if ( !callback ) {
throw new TypeError('undefined is not a function');
}
// Ensure the supplied callback is a function.
if ( typeof callback !== 'function' ) {
throw new TypeError(callback + " is not a function");
}
}
3. Execute the callback for each item in the array.
function newMap(callback, thisArg = undefined) {
// Ensure it called on an array.
if (!Array.isArray(this)) {
throw new TypeError('Must be called on an array');
}
// Ensure a callback was supplied.
if ( !callback ) {
throw new TypeError('undefined is not a function');
}
// Ensure the supplied callback is a function.
if ( typeof callback !== 'function' ) {
throw new TypeError(callback + " is not a function");
}
/**
* Make a copy of the original array, just in case the callback
* does any mutations on the current value.
*/
const self = Array.from(this);
// Initialize a new array to build and return.
let newArray = [];
// Loop through the length of the original array.
for (i = 0; i < this.length; i++) {
/**
* Execute the callback with `thisArg` as the `this` context.
* Callback is executed with 3 arguments:
* self[i] is the current value
* i is the current index
* this is the original array
*/
let result = callback.call(thisArg, self[i], i, this);
// Add the result of the callback to the new array.
newArray[i] = result;
}
// Return the new array.
return newArray;
}
Testing our new method.
We can attach our new method to the Array
prototype and test it out like this:
Array.prototype.newMap = newMap;
const arr = exercises.newMap(item => {
return {% raw %}`new ${item}`{% endraw %};
})
// Returns the same result as Array.map
[ 'new Bench Press',
'new Svend Press',
'new Chest Flyes',
'new Kettlebell Swing',
'new 40 Yard Sprint' ]
Interested in more tips and challenges? Follow me on Twitter and also check out my collection of previous tips and challenges.
Top comments (2)
Nice article, and I'm actually about to make a video on this exact same subject!
I noticed that when you do the
for
loop, there'si = 0
, instead oflet i = 0
, and this could result in the creation (or mutation) of a global variable. Funny enough,i
is a global variable on dev.to at the moment, and it's value is0
! But once you run the code above, it gets updated.Good job on this article!
Nice catch!