Why no Array.prototype.flatMap in javascript?
Lodash provides a flatmap function, which to me is practically equivalent to Javascript providing it natively. If you're not a Lodash user, then ES6's Array.reduce()
method can give you the same result, but you have to map-then-flatten in discrete steps.
Below is an example of each method, mapping a list of integers and returning only the odds.
Lodash:
_.flatMap([1,2,3,4,5], i => i%2 !== 0 ? [i] : [])
ES6 Reduce:
[1,2,3,4,5].map(i => i%2 !== 0 ? [i] : []).reduce( (a,b) => a.concat(b), [] )
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','♧']
// ]
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
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
.