Functional Cartesian Products in JavaScript
Published April 2, 2019
In a recent project I had two arrays of length m
and n
, combined together to create a new array of size m * n
. This is sometimes referred to as a Cartesian product, particularly in Set theory. Naive pseudo-code would look like:
fruits = []
for apple in apples:
for pear in pears:
fruits.push(apple + pear)
Since I was using JavaScript, it looked more like:
const fruits = []
for (const apple of apples) {
for (const pear of pears) {
fruits.push(`${apple} and ${pear}`)
}
}
However, I wanted to see if I could use some of the methods available in Array.prototype
coupled with ES6’s arrow functions to create a one-liner. Would be less readable, no doubt, but I get experience with more of the functional side of JavaScript, which I personally like.
Here’s what I came up with:
const fruits = apples.reduce((acc, apple) => acc.concat(pears.map(pear => `${apple} and ${pear}`)), [])
Let’s break that down:
Array.prototype.reduce
executes areduce
r function on each element in the array (ultimately returning a single value — in this case an array)Array.prototype.concat
allows you to merge (concat
enate) two or more arrays (implementing the.push()
from the original)Array.prototype.map
creates a new array by calling a function on each element (iterating over the elements in the 2nd array)
In “plain” English:
- Starting with an empty array (last parameter), referenced by
acc
, iterate over eachapple
inapples
. - For that
apple
,concat
enate a new array ontoacc
by: map
ping eachpear
ofpears
with the currentapple
.
This is a somewhat silly example — apples & pears — but I hope it gets the idea across. It’s admittedly much less readable, but there is an undeniable elegance to it.