Understand Array.map() in JavaScript by Rolling Your Own

The Array.map() function in JavaScript is a common stumbling block for beginner developers who are dipping their toes into the land of functional programming. After spending lots of time learning the ins and outs of all the different types of loops in JavaScript, wrapping your head around the concept of map can seem pretty foreign. This post will act as a general introduction, and (hopefully) a gateway into the powerful world of functional programming for any of you out there who haven't yet been exposed. If you're interested in an in depth exploration into functional programming in JavaScript, definitely check out Kyle Simpson's Functional Light JavaScript. If you're looking for something short, hands-on, and to the point, read on.

Map, Maps and Mapping

Map (not to be confused with the data structure, 'Map') is a functional utility we can use to iterate over an array, apply some kind of change to each value, and return a new array with the changed values. Put in more general terms, a mapping is simply a transformation of one value into another value. If I were to take the number 10 and add 5 to it, I've just mapped the value 10 into 15. Doing this for all values held in a list (array) and returning a new list would be considered mapping over the list.

Let's take a look at an example in code:

const arrayToMapOver = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

function timesFive(value) {
  return value * 5
}

const newArray = arrayToMapOver.map(timesFive)
// newArray is now [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
// arrayToMapOver is still [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

In this example, we start with an array of numbers 1 through 10. We then define a function, timesFive(), which takes a value and returns that value multiplied by five. Next, we take the Array.map() method which is available on all JavaScript arrays, and use it to apply timesFive() to every value contained inside of the array bound to the variable arrayToMapOver. Finally, we bind the output of the map method (which is also an array) to the variable called newArray.

A word on Immutability

It's important to note that even though we are mapping over every value contained in the arrayToMapOver array, none of the original values are changed. This is consistent with one of the main pillars of functional programming, 'Immutability'. Immutability is a bit outside the scope of this article, but basically it refers to the idea that data should not be mutated, or changed. Instead, when we need to modify existing data, we should re-build that data in a new form. In our case, we don't change the values in arrayToMapOver, instead we store the values returned by timesFive() in our newArray.

Rolling our own Map function

Explaining the concept of map is one thing, but this is a practical introduction so let's try building our own. First, we'll need to define the structure of our function:

function ourMap(transformation, inputArray) {
  // our map function logic will go here
};

Let's break this down. Our map function is not going to be attached as a method to any arrays, so it will need two parameters/arguments: transformation and inputArray. transformation will be the function that we apply to every value in our second argument, inputArray. With our function signature defined, let's add some logic to our map:

function ourMap(transformation, inputArray) {
  // bind an empty array to a variable to hold our transformed values
  let outputArray = [];

  return outputArray
};

Perfect. We've bound the variable outputArray to an empty array so we can eventually store our output values. We've also preemptively returned that output array. It's empty for now, but we'll soon get around to filling it. As a matter of preference, I used the let keyword to bind outputArray, instead of const. The only reason for this is that outputArray will necessarily be modified (added to), so declaring it as a const wouldn't make as much sense.

Now on to the next piece:

function ourMap(transformation, inputArray) {
  // bind an empty array to a variable to hold our transformed values
  let outputArray = [];

  // loop over the input array
  for (let value of inputArray) {
    // apply our transformation here
  }

  return outputArray
};

Wait what? A for loop? I thought this was functional programming?

Abstraction for the win

One of the main purposes of functional programming is to provide abstractions for often-used pieces of functionality in order to make the code clearer to the developer who is working on it. In theory, this abstraction simplifies the codebase and makes it easier to maintain in the long run. But, when you get right down to it, any code that runs on a computer at some level consists of basic operations, including assignment statements, if statements, and loops. Functional programs are no different.

Now for the final part:

function ourMap(transformation, inputArray) {
 // bind an empty array to a variable to hold our transformed values
 let outputArray = [];

 // loop over the input array
 for (let value of inputArray) {
   // apply our transformation here
   let transformedValue = transformation(value)

   // put the transformed value in our outputArray
   outputArray.push(transformedValue)
 }

 return outputArray
};

Here we simply take each value in our inputArray, via the handy for loop we added earlier, and apply the transformation argument (which is a function). We then take that transformedValue and push it into our outputArray. Presto, we've done it. The last line of ourMap returns our outputArray with all of our transformed values. If we replace the Array.map() method from before with ourMap implementation, it should work exactly the same:

function ourMap(transformation, inputArray) {
  // bind an empty array to a variable to hold our transformed values
  let outputArray = [];

  // loop over the input array
  for (let value of inputArray) {
    // apply our transformation here
    let transformedValue = transformation(value)

    // put the transformed value in our outputArray
    outputArray.push(transformedValue)
  }

  return outputArray
};

const arrayToMapOver = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

function timesFive(value) {
  return value * 5
}

const newArray = ourMap(timesFive, arrayToMapOver);
// newArray is still, whoohoo! [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]

And that's it! Hopefully implementing our own map function helped to really drill in how mapping works. If you found this article useful or informative, please consider sharing it. Also, if you're interested in taking a look at other implementations of functional programming concepts, definitely check out the Ramda Library.

Thanks for reading.

Show Comments

Get the latest posts delivered right to your inbox.