programación funcional - Eliminando elementos con Array.map en JavaScript
Me gustaría filtrar un arreglo de elementos utilizando la función map(). Aquí hay un fragmento de código:
var filteredItems = items.map(function(item)
{
if( ...some condition... )
{
return item;
}
});
El problema es que los elementos filtrados todavía usan espacio en el arreglo y me gustaría eliminarlos por completo.
Mi código específico no estaba destinado a ejecutarse en un navegador, así que no me preocupa la compatibilidad específica del navegador con ninguna de las dos funciones.
¿Alguna idea?
Respuesta #1
Deberías usar el método filter en lugar de map a menos que quieras mutar los elementos en el arreglo, además de filtrarlos.
p. ej.
var filteredItems = items.filter(function(item)
{
return ...some condition...;
});
[Editar: Por supuesto, siempre podrías hacer sourceArray.filter(...).map(...) para tanto filtrar como mutar]
Respuesta #2
Inspirado por escribir esta respuesta, terminé más tarde ampliando y escribiendo una publicación en el blog repasando esto con detalle cuidadoso. Recomiendo revisarla si quieres desarrollar una comprensión más profunda de cómo pensar sobre este problema: intento explicarlo pieza por pieza, y también doy una comparación en JSperf al final, repasando consideraciones de velocidad.
Dicho esto, **El resumen es este:
Para lograr lo que estás pidiendo (filtrar y mapear dentro de una sola llamada de función), usarías Array.reduce()**.
Sin embargo, el enfoque más legible y (menos importante) por lo general significativamente más rápido2 es simplemente usar filter y map encadenados:
[1,2,3].filter(num => num > 2).map(num => num * 2)Lo que sigue es una descripción de cómo funciona Array.reduce(), y cómo puede usarse para lograr filter y map en una sola iteración. Nuevamente, si esto es demasiado condensado, recomiendo encarecidamente ver la publicación del blog vinculada arriba, que es una introducción mucho más amigable con ejemplos claros y progresión.
Usted da a reduce un argumento que es una función (usualmente anónima).
Esa función anónima toma dos parámetros: uno (como las funciones anónimas pasadas a map/filter/forEach) es el iterador sobre el cual se operará. Sin embargo, hay otro argumento para la función anónima pasada a reduce, que esas funciones no aceptan, y ese es el valor que se pasará entre llamadas a la función, a menudo denominado memo.
Ten en cuenta que mientras Array.filter() solo toma un argumento (una función), Array.reduce() también toma un segundo argumento importante (aunque opcional): un valor inicial para 'memo' que se pasará a esa función anónima como su primer argumento y que posteriormente puede ser modificado y pasado entre llamadas de función. (Si no se proporciona, entonces 'memo' en la primera llamada de la función anónima será por defecto el primer elemento del array, y el argumento 'iteratee' será en realidad el segundo valor del array)
En nuestro caso, pasaremos un array vacío para empezar, y luego elegiremos si inyectar nuestro iterador en nuestro array o no en función de nuestra función; este es el proceso de filtrado.
Finalmente, devolveremos nuestro 'array en progreso' en cada llamada de función anónima, y reduce tomará ese valor de retorno y lo pasará como argumento (llamado memo) a su siguiente llamada de función.
Esto permite que filter y map ocurran en una sola iteración, reduciendo a la mitad nuestro número de iteraciones requeridas, aunque solo haciendo el doble de trabajo en cada iteración, por lo que realmente no se ahorra nada más que llamadas a funciones, que no son tan costosas en JavaScript.
Para una explicación más completa, consulta la documentación de MDN (o mi publicación mencionada al inicio de esta respuesta).
Ejemplo básico de una llamada a Reduce:
let array = [1,2,3];
const initialMemo = [];
array = array.reduce((memo, iteratee) => {
// if condition is our filter
if (iteratee > 1) {
// what happens inside the filter is the map
memo.push(iteratee * 2);
}
// this return value will be passed in as the 'memo' argument
// to the next call of this function, and this function will have
// every element passed into it at some point.
return memo;
}, initialMemo)
console.log(array) // [4,6], equivalent to [(2 * 2), (3 * 2)]
versión más concisa:
[1,2,3].reduce((memo, value) => value > 1 ? memo.concat(value * 2) : memo, [])
Observe que el primer iterador no era mayor que uno, y por lo tanto fue filtrado. También note el initialMemo, nombrado solo para dejar clara su existencia y llamar la atención sobre él. Una vez más, se pasa como 'memo' a la primera llamada de función anónima, y luego el valor devuelto de la función anónima se pasa como argumento 'memo' a la siguiente función.
Otro ejemplo del caso de uso clásico de memo sería devolver el número más pequeño o más grande en un arreglo. Ejemplo:
[7,4,1,99,57,2,1,100].reduce((memo, val) => memo > val ? memo : val)
// ^this would return the largest number in the list.
Un ejemplo de cómo escribir tu propia función reduce (esto a menudo ayuda a entender funciones como estas, me parece):
test_arr = [];
// we accept an anonymous function, and an optional 'initial memo' value.
test_arr.my_reducer = function(reduceFunc, initialMemo) {
// if we did not pass in a second argument, then our first memo value
// will be whatever is in index zero. (Otherwise, it will
// be that second argument.)
const initialMemoIsIndexZero = arguments.length < 2;
// here we use that logic to set the memo value accordingly.
let memo = initialMemoIsIndexZero ? this[0] : initialMemo;
// here we use that same boolean to decide whether the first
// value we pass in as iteratee is either the first or second
// element
const initialIteratee = initialMemoIsIndexZero ? 1 : 0;
for (var i = initialIteratee; i < this.length; i++) {
// memo is either the argument passed in above, or the
// first item in the list. initialIteratee is either the
// first item in the list, or the second item in the list.
memo = reduceFunc(memo, this[i]);
// or, more technically complete, give access to base array
// and index to the reducer as well:
// memo = reduceFunc(memo, this[i], i, this);
}
// after we've compressed the array into a single value,
// we return it.
return memo;
}
La implementación real permite el acceso a cosas como el índice, por ejemplo, pero espero que esto te ayude a obtener una sensación sencilla de la idea principal.
Respuesta #3
Eso no es lo que hace map. Realmente quieres Array.filter. O si realmente quieres eliminar los elementos de la lista original, vas a necesitar hacerlo de manera imperativa con un bucle for.
