Pregunta 1Después de ejecutar prices.map((price) => price * 2), ¿qué le pasa al prices original?
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
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.
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
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étodo | Valor de retorno del callback | Elementos del array resultante |
|---|---|---|
| map | Entra en el array nuevo | Los mismos que el original |
| filter | Si es true, el elemento se conserva | Los mismos que el original o menos |
| forEach | Se ignora | No 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.
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
.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.
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
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.
Verificación de conocimientos
Responde cada pregunta una a una.
Pregunta 2En courses.filter(isPublished).map(toLabel), ¿qué array lee map?
Pregunta 3En el callback de ranking.map((item, index) => ...), ¿qué recibe index?