Aprende leyendo en orden

map y filter — transformar un array o filtrarlo

Aprende map, que transforma cada elemento, y filter, que conserva solo los que cumplen una condición. Verás por qué el original no cambia, cómo encadenarlos y el argumento index.

Cuando sacas solo los precios con impuestos de un menú o te quedas con los productos que tienen existencias, siempre terminas escribiendo lo mismo: declarar un array vacío y llamar a push dentro de forEach.

En este artículo verás map, que transforma los elementos, y filter, que conserva solo los elementos que cumplen una condición.

Transformar cada elemento en un array nuevo — map

Supón que quieres la lista de precios de una cafetería con los impuestos ya incluidos. El resultado es un array con el mismo orden que los 3 elementos originales, en el que solo cambian los precios. Con forEach puedes calcular cada precio, pero para juntar los resultados tienes que crear tú el array que los recoge y encargarte de él hasta que termine el recorrido.

El método de array map (un método que pasa cada elemento por un callback y devuelve un array nuevo con los valores de retorno, en el mismo orden) se encarga tanto de crear el array de resultados como de recorrer los elementos. En la función que le pasas solo escribes cómo transformar un elemento, y devuelves el valor transformado.

const menuItems = [
  { name: "Café de la casa", price: 480 },
  { name: "Café latte", price: 540 },
  { name: "Cheesecake", price: 620 },
];

// Pon en una función solo la transformación de un elemento
const toTaxIncluded = (item) => Math.floor(item.price * 1.1);

// toTaxIncluded se llama una vez por elemento y sus valores de retorno forman el array nuevo
const taxIncluded = menuItems.map(toTaxIncluded);
console.log(taxIncluded.join(", "));   // 528, 594, 682
console.log(taxIncluded.length);       // 3
console.log(menuItems.length);         // 3
Cada valor devuelto queda en la misma posición
Una sola función:toTaxIncludedmenuItemsíndice 0: 480menuItemsíndice 1: 540menuItemsíndice 2: 620480 × 1.1 →devuelve 528540 × 1.1 →devuelve 594620 × 1.1 →devuelve 682taxIncludedíndice 0taxIncludedíndice 1taxIncludedíndice 2
Los 3 valores devueltos entran en taxIncluded en el mismo orden que los elementos originales. map no cambia el número de elementos: solo reemplaza sus valores.

map solo le pasa cada elemento al callback, uno por uno, y va agregando al array nuevo lo que este devuelve, así que el número de elementos no tiene forma de cambiar. Para filtrar, que sí cambia ese número, necesitas otro método.

forEach no te devuelve nada

forEach solo llama a la función que le pasas; no recoge sus valores de retorno. Siempre devuelve undefined, así que aunque escribas const list = menuItems.forEach(toTaxIncluded);, list vale undefined. Para obtener los resultados como un array, usa map.

Convierte los productos de una panadería en una lista de precios de oferta y una lista de nombres. bakeryItems ya está declarado.

① Crea un array salePrices con los precios de oferta (price por 0.8, redondeado hacia abajo).

② Muestra salePrices unido con ", ".

③ Crea un array solo con los nombres de los productos y muéstralo unido con " / ".

④ Muestra el price del primer elemento de bakeryItems.

(Si se ejecuta correctamente, aparecerá una explicación.)

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Conservar solo los elementos que cumplen una condición — filter y cuántos quedan

Supón que quieres listar solo los libros que tienen existencias. Como map no cambia el número de elementos, no sirve para quitar los agotados, y vuelves a escribir un if dentro de forEach para llamar a push.

filter (un método que devuelve un array nuevo solo con los elementos para los que el callback devolvió true) se encarga de este tipo de filtrado. La función que le pasas recibe un elemento y devuelve un booleano que indica si se conserva o no.

const books = [
  { title: "SQL práctico", stock: 0 },
  { title: "Redes desde cero", stock: 5 },
  { title: "Linux ilustrado", stock: 2 },
];

// Escribe una función que devuelva un booleano: conservarlo o no
const isInStock = (book) => book.stock > 0;

// Solo los elementos que devolvieron true entran en el array nuevo
const available = books.filter(isInStock);
console.log(available.length);      // 2
console.log(available[0].title);    // Redes desde cero
console.log(books.length);          // 3
Los elementos que quedan se desplazan hacia adelante
books índice 0stock 0stock > 0 esfalseNo entraen availablebooks índice 1stock 5stock > 0 estrueavailableíndice 0books índice 2stock 2stock > 0 estrueavailableíndice 1
Solo los 2 elementos que devolvieron true entran en available. El array se acorta en tantos elementos como se descartaron, y los que quedan se desplazan hacia adelante para ocupar los huecos.

En available solo están las posiciones nuevas; la posición que cada elemento tenía en books se pierde. Al final de este artículo verás cómo trabajar con la posición original. La tabla de abajo resume cómo usa cada uno de los 3 métodos el valor de retorno de la función que le pasas.

MétodoValor de retorno del callbackElementos del array resultante
mapEntra en el array nuevoLos mismos que el original
filterSi es true, el elemento se conservaLos mismos que el original o menos
forEachSe ignoraNo se crea ningún array (devuelve undefined)

Con llaves necesitas return

Si escribes el cuerpo entre llaves, como en (book) => { book.stock > 0 }, el valor de retorno es undefined a menos que escribas return. undefined es falsy, así que no se conserva ningún elemento y obtienes un array vacío. Si la condición cabe en una línea, escríbela sin llaves.

En una tienda en línea de plantas de interior, separa la lista de productos según el tamaño de la maceta. plants ya está declarado.

① Crea un array largePots solo con los productos de maceta grande y muestra cuántos hay.

② Muestra los nombres de los productos que quedaron en largePots, unidos con ", ".

③ Crea un array smallPots solo con los productos de maceta chica y muestra sus nombres unidos con ", ".

④ Muestra el número de elementos de plants.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Filtrar y luego transformar — encadenar filter y map

Supón que quieres convertir solo los cursos publicados en textos para mostrar en pantalla. Podrías guardar el resultado de filter en una variable y llamar a .map(...) sobre ella en una segunda línea, pero así le pones nombre a un array intermedio que no vas a volver a usar.

filter también devuelve un array, así que puedes escribir .map(...) directamente sobre su valor de retorno. Escribir las llamadas a métodos una tras otra de esta forma se llama encadenamiento (llamar al siguiente método sobre el valor de retorno del método anterior).

const courses = [
  { title: "Git desde cero", minutes: 45, published: true },
  { title: "Práctica de SQL", minutes: 90, published: false },
  { title: "Linux básico", minutes: 60, published: true },
];

// Conserva solo los publicados y convierte el resultado en textos para mostrar
// Un . al inicio de la línea llama al método sobre el resultado de la línea anterior (sigue siendo una sola expresión)
const labels = courses
  .filter((course) => course.published)   // published ya es un booleano, así que se devuelve sin comparar
  .map((course) => `${course.title} (${course.minutes} min)`);

console.log(labels.join(" / "));
// Git desde cero (45 min) / Linux básico (60 min)
console.log(labels.length);    // 2
console.log(courses.length);   // 3
Los 3 arrays de una cadena de métodos
courses3 objetosEl originalsigue con 3.filter(...)2 objetosNunca recibenombre.map(...)2 cadenasSe guardaen labels
.map lee el array que devolvió filter sin que ese array llegue a tener nombre. Cada paso de la cadena crea un array nuevo.

Si quieres revisar el resultado intermedio, guarda el resultado de filter en una variable, muéstralo con console.log y después llama a .map(...) sobre esa variable. Cuando termines de revisarlo, puedes volver a la forma encadenada.

Todos reciben 5 puntos extra por asistir a una clase de recuperación. Compara cada puntaje ajustado con la nota mínima para aprobar, que es 75. records ya está declarado.

① Con una cadena de métodos, crea un array passedScores que sume 5 puntos al puntaje de todos y luego conserve solo los de 75 o más.

② Muestra passedScores unido con ", ".

③ Muestra el score del segundo elemento de records.

④ Con una cadena de métodos, obtén los nombres de quienes llegan a 75 o más con los puntos extra y muéstralos unidos con " / ".

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Obtener la posición — el index del callback

Supón que quieres añadir el puesto, como "Puesto 1" o "Puesto 2", a una lista de ranking. Los elementos no guardan su puesto, así que es tentador poner una variable contador fuera del callback y sumarle 1 en cada llamada.

map y filter pasan el elemento como primer argumento del callback, y la posición de ese elemento (su índice, contado desde 0) como segundo. Los argumentos se asignan por orden, así que aunque solo necesites la posición, tienes que escribir el primer parámetro. Si recibes esa posición con el nombre index, puedes usarla tanto para mostrar el puesto como para comprobar si un elemento está entre los primeros.

const ranking = ["Audífonos inalámbricos", "Batería portátil", "Soporte para celular", "Hub USB"];

// El segundo argumento es la posición, contada desde 0
const lines = ranking.map((item, index) => `Puesto ${index + 1}: ${item}`);
console.log(lines.join("\n"));
// Puesto 1: Audífonos inalámbricos
// Puesto 2: Batería portátil
// Puesto 3: Soporte para celular
// Puesto 4: Hub USB

// El callback de filter recibe los mismos 2 argumentos en el mismo orden
const topThree = ranking.filter((item, index) => index < 3);
console.log(topThree.length);   // 3
Dos usos del mismo index
map recibe(item, index)Los 4 elementosreciben de 0 a 3index + 1 entraen el textoSiguen siendo 4,ya con puestofilter recibe(item, index)El mismo 0 a 3que en mapSe conservasi index < 3Quedan 3, convalores intactos
Los dos callbacks reciben como segundo argumento la posición, contada desde 0. map incorpora la posición al valor nuevo, y filter la usa para decidir qué conservar.

El index que recibe filter es la posición en el array original. Que los elementos conservados se desplacen hacia adelante después no afecta la comprobación, así que index < 3 se refiere a los 3 primeros elementos del array original.

Una página de artículos destacados los muestra en orden de recomendación. articles y viewCounts ya están declarados, y viewCounts guarda las visitas de cada artículo en el mismo orden.

① Usando viewCounts, crea un array de textos con la forma "Título (N visitas)" y muéstralos uno por línea.

② Conserva solo los artículos con 5000 visitas o más y muéstralos unidos con ", ".

③ Conserva solo los artículos que ocupan un puesto par, como el 2 y el 4, y muéstralos unidos con ", ".

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1Después de ejecutar prices.map((price) => price * 2), ¿qué le pasa al prices original?

Pregunta 2En courses.filter(isPublished).map(toLabel), ¿qué array lee map?

Pregunta 3En el callback de ranking.map((item, index) => ...), ¿qué recibe index?