flatMap
es increíblemente útil en colecciones, pero javascript no proporciona uno mientras tiene Array.prototype.map
. ¿Por qué?
¿Hay alguna forma de emular flatMap
en javascript de manera fácil y eficiente sin definir flatMap
manualmente?
javascript
functional-programming
Sergey Alaev
fuente
fuente
arr.reduce((arr, v) => (arr.push(...v), arr), [])
?arr.map(...).reduce(...)
)..map
ping.Respuestas:
Actualización:
Array.prototype.flatMap
está en camino a ECMAScript nativo. Actualmente se encuentra en la etapa 4 .Es ampliamente compatible en muchos entornos. Vea si funciona en su navegador usando este fragmento a continuación:
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 ]
Porque la programación no es mágica y todos los lenguajes no tienen características / primitivas que todos los demás lenguajes tienen
Lo que importa es
JavaScript te da la posibilidad de definirlo por tu cuenta
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))
O una reescritura que colapsa los dos bucles en uno
const flatMap = (f,xs) => xs.reduce((acc,x) => acc.concat(f(x)), []) const xs = [1,2,3] console.log(flatMap(x => [x-1, x, x+1], xs))
Si lo quieres
Array.prototype
, nada te detieneconst concat = (x, y) => x.concat(y) const flatMap = (f, xs) => xs.map(f).reduce(concat, []) if (Array.prototype.flatMap === undefined) { Array.prototype.flatMap = function(f) { return flatMap(f, this) } } const xs = [1, 2, 3] console.log(xs.flatMap(x => [x-1, x, x+1]))
.as-console-wrapper { top: 0; max-height: 100% !important; }
fuente
Array.prototype._mylib_flatMap
usar el espacio de nombres para cualquier adición a los prototipos (por ejemplo ), o incluso mejor usar los símbolos ES6, en lugar de simplemente definirArray.prototype.flatMap
.map
una función definida como global! La segunda es solo una mala prácticaappend
,map
,filter
,foldl
,foldr
entre muchos otros en el espacio de nombres predeterminado. No lo convierte en una mala práctica porque escuchaste a alguien decir "los globales son malos" o "extender los nativos es malo"; el raqueta está entre los lenguajes mejor diseñados. Simplemente no sabes cómo / cuándo hacerlo adecuadamente.flatMap
ha sido aprobado por el TC39 como parte de ES2019 (ES10). Puedes usarlo así:[1, 3].flatMap(x => [x, x + 1]) // > [1, 2, 3, 4]
Aquí está mi propia implementación del método:
const flatMap = (f, arr) => arr.reduce((x, y) => [...x, ...f(y)], [])
Artículo de MDN sobre flatMap
fuente
flapMap
? ¡Quiero aplanar mi mapa, no agitarlo!const flatMap = (arr, ...args) => [].concat(...arr.map(...args));
(o usarapply
si no puede usar el operador de propagación), aunque esta versión concat tiene problemas de tamaño de pila de llamadas para matrices enormes como se menciona en una solución inferiorSé que dijiste que no querías definirlo tú mismo, pero esta implementación es una definición bastante trivial.
También hay esto de la misma página de github:
Aquí hay una forma un poco más corta usando es6 spread, similar al de renaudtertrais, pero usando es6 y no agregando al prototipo.
var flatMap = (a, cb) => [].concat(...a.map(cb)) const s = (v) => v.split(',') const arr = ['cat,dog', 'fish,bird'] flatMap(arr, s)
¿Ayudaría alguno de estos?
Cabe señalar (gracias a @ftor) que esta última "solución" sufre del "tamaño máximo de pila de llamadas excedido" si se llama en una matriz realmente grande (por ejemplo, 300k elementos)
a
.fuente
[].concat(...(new Array(300000).fill("foo").map(x => x.toUpperCase())))
. Claro, es un caso de esquina. Pero al menos deberías mencionarlo.Lodash proporciona una función de mapa plano, que para mí es prácticamente equivalente a que Javascript la proporcione de forma nativa. Si no es un usuario de Lodash, el
Array.reduce()
método de ES6 puede darle el mismo resultado, pero debe mapear y luego aplanar en pasos discretos.A continuación se muestra un ejemplo de cada método, mapeando una lista de números enteros y devolviendo solo las probabilidades.
Lodash:
_.flatMap([1,2,3,4,5], i => i%2 !== 0 ? [i] : [])
ES6 Reducir:
[1,2,3,4,5].map(i => i%2 !== 0 ? [i] : []).reduce( (a,b) => a.concat(b), [] )
fuente
Un enfoque bastante conciso es hacer uso de
Array#concat.apply
:const flatMap = (arr, f) => [].concat.apply([], arr.map(f)) console.log(flatMap([1, 2, 3], el => [el, el * el]));
fuente
Hice algo como esto:
Array.prototype.flatMap = function(selector){ return this.reduce((prev, next) => (/*first*/ selector(prev) || /*all after first*/ prev).concat(selector(next))) }
[[1,2,3],[4,5,6],[7,8,9]].flatMap(i => i); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
[{subarr:[1,2,3]},{subarr:[4,5,6]},{subarr:[7,8,9]}].flatMap(i => i.subarr); //[1, 2, 3, 4, 5, 6, 7, 8, 9]
fuente
¡Ahora tenemos
flatMap()
en Javascript! Y se soporta bastante bienconst dublicate = x => [x, x]; console.log([1, 2, 3].flatMap(dublicate))
fuente
{ "message": "Uncaught TypeError: [1,2,3].flatMap is not a function", "filename": "https://stacksnippets.net/js", "lineno": 15, "colno": 23 }
Array.prototype.flatMap()
ha llegado ahora a JS. Sin embargo, es posible que no todos los navegadores los admitan. Compruebe la compatibilidad actual del navegador con los documentos web de Mozilla .Lo que hace el
flatMap()
método es primero mapea cada elemento usando una función de devolución de llamada como argumento, luego aplana el resultado en una nueva matriz (la matriz 2d ahora es 1d ya que los elementos se aplanaron).Aquí también hay un ejemplo de cómo usar la función:
let arr = [[2], [4], [6], [8]] let newArr = arr.flatMap(x => [x * 2]); console.log(newArr);
fuente