forEach vs map

forEach y map son dos métodos que se parecen bastante en cuanto a la forma de usarlos, pero que no valen para lo mismo. Veamos sus diferencias semánticas.

Supongo que sabes que en JavaScript, existe un tipo de datos llamado array, que representa una colección de valores. Por ejemplo, las distintas filas de una tabla, o los distintos precios de un carrito de la compra.

Hoy en día, la biblioteca estandar de JavaScript es tan rica en funciones, que si pones un array (o un identificador con la variable por la que viene), y pulsas el punto, verás un montón de métodos o propiedades que puedes usar para hacer cosas con tu array, como sacar su longitud (con length).

Si necesitas iterar sobre los elementos de tu colección de forma funcional, puedes utilizar métodos como forEach para recorrer cada uno de esos elementos de la colección. Y ahí es por donde quiero empezar.

¿Para qué sirve el método forEach?

Este método es como un bucle for tradicional, de los que pones con la palabra clave for. Sin embargo, este es funcional.

Como parámetro, le tienes que pasar una función de primera clase, donde entregues a la función forEach el callback con lo que sea que quieres que ocurra cuando se invoque.

Este callback recibe uno o dos parámetros. Luego te cuento del segundo, pero por ahora vamos a imaginar que solo recibe uno. Por lo que, con lo que te digo hasta ahora, tiene la siguiente forma:

const precios = [12, 14, 10, 8];
precios.forEach((precio) => {

});

Como ves, forEach es una función invocable para precios, que es un array. En el callback, acepto un parámetro llamado precio, y el cuerpo de la función está en blanco porque ahora tengo que crear.

forEach se comporta como un for, así que si ya sabes usar un bucle for, esto no te sonará nuevo. Simplemente pones dentro del cuerpo de ese callback lo que sea que quieres que ocurra.

Por ejemplo, un console.log:

const precios = [12, 14, 10, 8];
precios.forEach((precio) => {
  console.log(`El precio de este producto es ${precio}`);
});

O un appendChild(), para insertarlo en el navegador web:

const precios = [12, 14, 10, 8];
precios.forEach((precio) => {
  const parrafo = document.createElement('p');
  parrafo.innerText = `El precio es ${precio}`;
  document.body.appendChild(parrafo);
});

Y hasta aquí todo correcto. Funciona igual que el for de toda la vida.

¿Qué hace que forEach sea mejor que un for normal?

No muchas cosas, la verdad. Son bastante equivalentes. En cualquier caso, si te gusta más la programación funcional, y te apasionan los lambdas (si es que a alguien le puede apasionar algo tan concreto), tal vez los aprecies más.

De todos modos, ya que lo preguntas, creo que hay un caso donde te puede gustar más usar forEach que usar un for normal.

Imagina que tienes un array de elementos que debes recorrer, pero que unas veces vas a querer recorrer con una lógica distinta. Por ejemplo, aquí te dejo este ejemplo un poco más avanzado que requiere saber cómo usar un ternario, pero que muestra esta faceta:

const accion = modoSimulacion ?
  mostrarEnConsola :
  enviarPeticion;
datos.forEach(accion);

En este caso, cuando la variable modoSimulacion valga true (o un truthy), el identificador accion se asociará con la función mostrarEnConsola. En cambio, si modoSimuacion vale false (o un falsy), entonces el identificador accion se asociará con la función enviarPeticion.

De modo que, en conclusión, cuando el modo simulación esté activo, se invocará el callback mostrarEnConsola, mientras que cuando el modo simulación esté apagado, se invocará el callback enviarPeticion, porque es lo que estás pasándole a datos via el forEach.

Así que no es que forEach haga las cosas diferentes a for, pero sí que cambia la intención, porque separa el cómo recorres el array (que no lo haces tú, lo hace la función forEach) de lo que haces.

En cuanto a lo del segundo parámetro...

Ah, te dije que forEach aceptaba en su callback un segundo parámetro.

Ese segundo parámetro se lo pones a forEach cuando quieras conocer por qué iteración del bucle vas. Eso es todo. El segundo parámetro de forEach es opcional, pero si se lo indicas, vas a recibir mediante un índice, por cuál iteración del bucle vas.

Empieza a contar en 0, y por cada iteración, se va incrementando en 1. En general, está bien para cuando necesites saber por qué iteración vas. No deberías necesitar una variable adicional que inicies a 0 por fuera y que vayas incrementando iteración a iteración, porque ya te la da JavaScript:

const precios = [12, 14, 10, 8];
precios.forEach((precio, idx) => {
  console.log(`El producto ${idx} vale ${precio}`);
});

Esto escribirá:

El producto 0 vale 12
El producto 1 vale 14
El producto 2 vale 10
El producto 3 vale 8

Transformaciones: la función map

Si entendiste la función forEach, ahora vamos con la map. Esta la usamos cuando quieres transformar los elementos de un array, es decir, cuando quieres convertir cada elemento en otro distinto.

Te voy a dar un ejemplo. Imagina que los precios del array que he estado usando hasta ahora son sin impuestos, pero necesitas calcular los impuestos. Voy a poner el ejemplo de España, porque no sé cómo son las cosas en otros países. Pero aquí, cuando compras algo en cualquier sitio, se le encarece el precio un 21% en concepto de impuestos. De modo que un producto no vale 10 euros, al final vale 12,10 euros en cuanto le sumas impuestos. (O dicho de otro modo, si te cobran 10 euros, es que el producto en realidad vale 7,9 euros).

Supongo que si no te contase de la existencia del map, podrías ponerle impuestos a los precios del siguiente modo:

const precios = [12, 14, 10, 8];
const preciosConIVA = [];
precios.forEach((precio) => {
  preciosConIVA.push(precio * 1.21);
});

Pero esto queda sucio, porque al final estás tocando desde dentro del callback una variable que está fuera. Se te complicarían las cosas si intentases pasar la función como parámetro, como te contaba un poco más arriba.

Aquí lo que estás haciendo es una transformación. Conceptualmente lo que quieres es convertir un array en otro. Antes era un array sin impuestos, ahora es un array con impuestos.

Y para esas transformaciones es que tienes el map, que es otra función distinta que puedes invocar para un array, y que se parece a forEach en que también recibe un callback como parámetro, pero que tiene otras implicaciones distintas.

Para empezar, map devuelve cosas. Es decir, que lo normal va a ser que asignes el resultado de llamar a la función map a una variable, para recoger el array transformado. map NO toca el array original, por lo que si no lo guardas a una variable, perderás la transformación. Esto puede parecer incómodo, pero en realidad es lo más seguro, porque te promete que tus datos originales van a quedar intactos.

Y por otra parte, el callback que le pasas a map también tiene que tener su propio return. Así es como JavaScript recibirá, para cada elemento del array, la transformación que quieras. Y esas transformaciones se pondrán en el mismo orden que el array original. Eso quiere decir que en el array resultante, el elemento [0] será la transformación del elemento [0] del array original; el elemento [7] será el del elemento [7], y así con todas.

Aquí tienes un ejemplo de cómo sería lo que te puse antes, pero con map:

const precios = [12, 14, 10, 8];
const preciosConIVA = precios.map((precio) => {
  return precio * 1.21;
});

¡Oye, miserable! Mi influencer favorito de JavaScript me ha dicho que no ponga return si no hace falta. Tranquilo, pataliebre. Ahora lo simplificamos:

const precios = [12, 14, 10, 8];
const preciosConIVA = precios.map((precio) => precio * 1.21);

Ahora sí ha quedado corto, pero me parecía prudente que vieses esa palabra: return.

Yo lo que sí necesito que entiendas es que ese callback devuelve cosas. JavaScript lo va a capturar, y con cada elemento que devuelva tras llamar a esa función con cada entrada, va a componer el nuevo array, que es lo que finalmente se devuelve como parte de la llamada a map.

Entonces al final la variable preciosConIVA tendrá como valor [14.52, 16.94, 12.1, 9.68], que es lo que obtienes si multiplicas por 1.21 cada elemento de precios.

¿Puedo ponerle return a un forEach?

No. Ni dentro del callback ni fuera. JavaScript va a ignorar lo que sea que devuelva tu callback. Y tampoco te va a devolver nada como parte de la llamada a map, así que tu variable acabará valiendo undefined.

¿Y cuando uso una u otra?

Yo pensaba que ya había quedado claro, pero lo recalco.

  • forEach cuando solo quieres iterar y que pasen efectos colaterales (como que se agreguen elementos al DOM, o llamar a una función que haga un API call, o que escriba por consola o algo así).
  • Y map lo usas cuando quieres transformar unas cosas en otras, más que causar efectos colaterales.

(Oye, ahora que ya van varias secciones y que te acabo de plantar una lista de puntos. Este artículo está escrito a mano, no lo ha hecho ChatGPT. Y me duelen las manos de teclear, pero paciencia que ya queda poco.)

Algunos ejemplos de cuándo te va a interesar usar map deberían incluir al menos cuando en React conviertes un array de objetos en una colección de nodos o componentes.

Por ejemplo:

const Listado = ({ nombres }) => (
  <ul>
  {nombres.map((nombre, i) => <li key={i}>{nombre}</li>)}
  </ul>
);

Por supuesto, el resto de reglas de React aplican aquí: pon una key de verdad, organiza mejor tu código y toda esa letra pequeña que aplicaría en este caso. Pero fíjate que está entrandole un array (que vamos a imaginar que es de strings, con los nombres) y estoy transformándolo en toda una colección de puntos que encajan bien con la lista que pinta ese componente.

¿Eso es todo?

Sí. A partir de aquí ya puedes ir a escribir tu código.

Si quieres un ejercicio, te animo a que pruebes a reimplementar estas funciones usando un for tradicional. Piensa que JavaScript al brindarte estas funciones, lo único que está haciendo es darte una forma conveniente de que puedas iterar o convertir cosas sin que te inmiscuyas en programar el bucle for y detallar exactamente cómo recorrer el bucle elemento a elemento. Sólo le dices a JavaScript lo que quieres que ocurra, y ya se ocupa de que eso se invoque.

Pero es un buen ejercicio de entrenamiento avanzado que hagas tu propia función map y tu propia función forEach. Pruébalo. En una función aparte, recibe un array como parámetro y escribe el bucle for(). Así también puedes probar a invocar lambdas. Recuerda pasar el segundo parámetro a tu callback.

Y para el map, no te olvides de ir capturando los retornos de cada función y de devolverlo todo. Y con eso ya estaría.

Lista de reproducción
  1. 1
    forEach vs map
    13 minutos
  2. 2
    El tutorial definitivo de promesas
    33 minutos
  3. 3
    Aprende a usar reduce en JavaScript
    21 minutos
  4. 4
    Cómo crear tests en NodeJS 20 sin instalar dependencias
    10 minutos
  5. 5
    Primeros pasos con AlpineJS, el microframework mágico
    9 minutos