The map method
Exploring functional javascript
4 min. read

A few weeks ago I published a blogpost in which I presented a functional code kata in Javascript and explained how I solved it using build-in Javascript methods (e.g. Array.prototype.map). However, it is worth to have a look at different implementations of these concepts in third party libraries, because there are slight but important differences.

In this blogpost, we have a closer look at the map function. First of all: What is map actually? It is a function that takes an iterable (e.g. an array) as argument and applies a callback on each of the containing values. The original data doesn’t get mutated, instead the result is returned as a new value.

const square = (n) => n * n
const numbers = [1, 2, 3]

const result = numbers.map(square)
console.log(result) // [1, 4, 9]

In general, all objects, where map can be invoked on, are called functors. So, in this case, numbers is a functor, because it can be mapped over.

Let’s compare three different implementations of map: The one, that comes on the array prototype (shown in the example above) and the ones from lodash and Ramda.

Basic usage

All three map implementations come with a different signature:

const _ = require('lodash')
const R = require('ramda')

const square = (n) => n * n
const numbers = [1, 2, 3]

// standard Javascript:
numbers.map(square) // => [1, 4, 9]

// lodash:
_.map(numbers, square) // => [1, 4, 9]

// Ramda: (both ways are possible)
R.map(square, numbers) // => [1, 4, 9]
R.map(square)(numbers) // => [1, 4, 9]

Data structures

One major difference are the types, that can be mapped over and the types that get returned. In standard Javascript, the map method is exclusively available on the Array prototype, so it is not possible to apply this functionality to an object or any other value – at least not without transforming them to an array upfront.

This is, where both lodash and Ramda take a more powerful approach: They can handle other types, lodash even supports null and undefined. However, they behave a bit differently in the data structure that they return. lodash consistently returns an array, no matter what you input. Ramda on the other hand keeps the original structure in the certain case where the functor is an object.

// Object
const prices = {shoes: 60, pants: 80}
const applyRebate = (o) => o * 0.5
_.map(prices, applyRebate) // => [30, 40]
R.map(applyRebate, prices) // => {shoes: 30, pants: 40}

// String
const name = 'Peter'
const uppercase = (s) => s.toUpperCase()
_.map(name, uppercase) // => ['P', 'E', 'T', 'E', 'R']
R.map(uppercase, name) // => ['P', 'E', 'T', 'E', 'R']

// Undefined
const identity = (x) => x
_.map(undefined, identity) // => []
R.map(identity, undefined) // => ERROR!

Currying

A basic idea in functional programming is that functions can be curried. That means, you don’t need to pass all arguments at once, but you can pass them one after the other in subsequent calls. The actual function body is invoked just with the last call. That way, it is possible to “configure” functions and pass them around. Currying is the main reason why Ramda has a different order of the arguments (compared to lodash or Array.prototype). It’s also possible to curry functions in lodash, in which case you will have to import it with require('lodash/fp') (read this guide).

const square = (n) => n * n
const numbers = [1, 2, 3]

const squareMap = R.map(square)
console.log(typeof squareMap) // => function

squareMap(numbers) // => [1, 4, 9]

Currying looks somewhat magical at the first glance, but when you have a closer look, it is a rather simple concept that allows higher decoupling and composability of code. Let’s consider the applyRebate function above, where the rebate of 0.5 was hardcoded, which makes it super specific. We can improve this by using a higher-order function1 that can be curried. Since Ramda only passes the actual value to our callback, we don’t need to implement something simple like applyRebate ourselves – instead, we just use the multiply function and are able to write it like that:

const prices = {shoes: 60, pants: 80}
const res = R.map(R.multiply(0.5), prices)

What’s next?

The comparisons spotted in this blogpost show the most basic and obvious differences between standard Javascript, lodash and Ramda. But there still remain (as usual) certain edge cases, that you may stumble across some time. One is pointed out in this piece written by Reg Braithwaite.


  1. Functions that take another function as argument or return one are called “higher-order function”. This is also referred to as “function as data”. ↩︎

My e-mail is: (Click anywhere to close.)