Why no Array.prototype.flatMap in javascript?

Solution 1:

Update: Array.prototype.flatMap made it into ES2019

It is widely supported in many environments. See if it works in your browser using this snippet below -

const data =
  [ 1, 2, 3, 4 ]
  
console.log(data.flatMap(x => Array(x).fill(x)))
// [ 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 ]

"Why no Array.prototype.flatMap in javascript?"

Because programming isn't magic and every language doesn't have features/primitives that every other language has. What matters is JavaScript gives you the ability to define it on your own -

const concat = (x,y) =>
  x.concat(y)

const flatMap = (f,xs) =>
  xs.map(f).reduce(concat, [])

const xs = [1,2,3]

console.log(flatMap(x => [x-1, x, x+1], xs))

Or a rewrite that collapses the two loops into one -

const flatMap = (f, xs) =>
  xs.reduce((r, x) => r.concat(f(x)), [])

const xs = [1,2,3]

console.log(flatMap(x => [x-1, x, x+1], xs))

If you want it to extend the Array.prototype, nothing is stopping you -

if (!Array.prototype.flatMap) {
  function flatMap (f, ctx) {
    return this.reduce
      ( (r, x, i, a) =>
          r.concat(f.call(ctx, x, i, a))
      , []
      )
  }
  Array.prototype.flatMap = flatMap
}

const ranks =
  [ 'J', 'Q', 'K', 'A' ]
  
const suits =
  [ '♡', '♢', '♤', '♧' ]

const result =
  ranks.flatMap(r =>
    suits.flatMap(s =>
      [[r, s]]
    )
  )

console.log(JSON.stringify(result))
// [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧']
// , ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧']
// , ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧']
// , ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧']
// ]

Solution 2:

flatMap has been approved by the TC39 as part of ES2019 (ES10). You can use it like this:

[1, 3].flatMap(x => [x, x + 1]) // > [1, 2, 3, 4]

Here's my own implementation of the method:

const flatMap = (f, arr) => arr.reduce((x, y) => [...x, ...f(y)], [])

MDN Article on flatMap

Solution 3:

I know you said you didn't want to define it yourself, but this implementation is a pretty trivial definition.

There's also this from the same github page:

Here is a bit of shorter way using es6 spread, similiar to renaudtertrais's - but using es6 and not adding to the prototype.

var flatMap = (a, cb) => [].concat(...a.map(cb))

const s = (v) => v.split(',')
const arr = ['cat,dog', 'fish,bird']

flatMap(arr, s)

Would either of these help?

It should be noted (thanks to @ftor) that this latter "solution" suffers from "Maximum call stack size exceeded" if called on a really large (e.g., 300k elements) array a.