Have you ever eagerly clicked on an article about functional programming (FP) in JavaScript only to be introduced to the map
, filter
, and reduce
array methods? No? Well, now you have!
In all seriousness, there are probably hundreds of posts out there about these three array methods and how they make your code more declarative and don't mutate yada yada yada. I even gave a talk along these same lines about two years ago. But even back then, I was itching for more. The promises of FP seemed way too grandiose to be summarized in three small methods.
Today we'll take a closer look at map
and filter
and why exactly they are so often discussed in the FP world. Spoiler alert: the nature of arrays (or lists) as ordered collections makes them powerful constructs which are fundamental to FP.
Filter first
First we'll look at filter. If you want a new array that contains all the values that meet a certain condition, filter greatly simplifies your code:
const array = [1, 2, 3, 4, 5];
const predicate = (number) => number % 2 === 0;
// Without filter
const newarray = [];
for (let i = 0; i < array.length; i++) {
if (predicate(array[i])) {
newarray.push(array[i]);
}
}
// With filter
const newarray = array.filter(predicate);
There are very few reasons to use the for
loop implementation over the filter method. Most of the time, when filtering, you need to maintain the original list, so mutating the list is a bad idea to begin with. We've already discussed the value of immutability, so we won't dig into this one any further.
Map reintroduced
Last time, we talked about how FP provides fundamental abstractions designed (or discovered) by mathematicians to produce declarative code. Without further ado, here is the abstraction that map
provides:
const array = [1, 2, 3, 4, 5]
const func = (number) => number * 2
// Without map
const newarray = []
for (let i = 0; i < array.length; i++) {
newarray.push(func(array[i]))
}
// With map
const newarray = array.map(func)
So basically, if you have an array of things, and you want a new array of things that has had a certain function applied to each one, you can use map
. Notice that there is no need for an index, and the action of pushing new elements to a predefined array is also gone.
This is certainly a useful utility on its own, but why all the fuss in the FP world? There is a particularly useful mathematical construct from category theory called a functor. A functor is an object (technically algebraic data structure) that has a map
(sometimes called fmap
for functor map) method that follows certain rules. Since Array.map
just so happens to follow these rules, FP people get super excited. Look! It's a functor! Isn't that exciting?
There are a bunch of other useful functors, but this is the only one that is built into JavaScript itself. (Technically Set
also has a map method, but Map
doesn't, if that wasn't confusing enough.) Each functor provides a different set of superpowers. Arrays let you represent an ordered collection of things. There are functors that allow you to store values with built in null checks, handle errors, deal with asynchronous values, and more.
Order and Expressiveness
But lets get back to the Array.map
method. I mentioned that arrays allow you to represent an ordered collection of things. The key word there is order. Anything that can be ordered can be represented in an array and mapped over. This includes the top to bottom linear execution of code itself. Lisp and other dialects (Closure, Racket, Scheme, etc.) are built on the fundamental principal that any evaluation can be represented as a list. Lambda calculus, which Lisp is based on, takes this a step further and represents every value as a list as well.
Expressiveness in a programming language all depends on how powerful the fundamental building blocks are. A Lisp interpreter can famously be implemented in Lisp itself in just a handful of lines. Lists are fundamental to programming itself because they enable the expression of order.
So in the end, you don't need to know anything about functors or abstract representations of order to use the map
method effectively in JavaScript. In defense of those hundreds of articles introducing map
, filter
, and reduce
, these methods are truly powerful and fundamental, but perhaps not for the reasons you may have initially thought of.
To summarize, use filter
when you want a subset of another array that meets a certain criterion and map
when you want an array of the same length with elements that have been transformed in some way by running a function over them. The map
method is worth talking about because:
- it is a useful abstraction of transformation of values in an array.
- it is an example of a functor, which is a fundamental algebraic data structure from category theory.
- lists themselves are powerful representations of ordering around which an entire model for computing can be expressed.
There is a lot more to functional programming than these methods, so stay tuned! Usually reduce
is introduced along with these two methods, but it is such a powerful construct that it deserves a post of its own.
Top comments (2)
Great series on FP :)
Thank you for following along!