🇺🇦 Слава Україні! Consulta cómo puedes ayudar a Ucrania desde España u otros países en supportukrainenow.org.

JavaScript explicado fácil

forEach vs map

• Duración: 12:53 • #javascript #foreach #map #funcional

forEach es un método funcional de la clase Array de JavaScript que sirve para recorrer los elementos de un Array. map es otro método que sirve para aplicar transformaciones. Aunque ambos iteran sobre los elementos de un bucle, su propósito es diferente y como tal, no hay que confundirlos y usarlos para lo que no toca.

Este vídeo lamentablemente todavía no tiene notas de episodio. Todavía quedan algunos vídeos en este sitio web que no tienen artículos de texto por si te gusta más leer que ver vídeos, y parece que esta página es una de ellas. Hay una transcripción del audio disponible para ayudarte a llegar a este vídeo que puede que te ayude.

¡Ayúdame a saber qué es prioritario y qué no! ¿Te hubiese ayudado encontrar un artículo de texto en esta página junto al vídeo?

Desplegar transcripción del episodio

Hola a todas, hola a todos, bienvenidos un día más a makigas. Os prometí que si le dábais al botón Me gusta al short que subí la semana pasada donde hablaba de forEach versus map, haría un vídeo un poco más largo. Aquí lo tenéis. En este tutorial os voy a contar la principal diferencia entre forEach y map, que es lo que provoca que mucha gente que está aprendiendo JavaScript confunda ambos métodos y no los use de la forma que hay que utilizarlos.

Os voy a poner en situación: es bastante probable que a estas alturas ya sepáis que en JavaScript existe un tipo de datos que es el array, que representa una colección de valores. Valores que están todos juntos dentro de la misma variable. Por ejemplo, si yo declaro una variable llamada verduras, dentro puede tener un array si escribo entre corchetes una serie de elementos separados por comas. En este caso voy a poner patata, puerro, boniato, coliflor, tomate; aunque bajo mi punto de vista el tomate no sea verdura.

Hoy día, la biblioteca estandar de JavaScript es tan rica en funciones, que si tienes una variable de tipo array, tienes al pulsar el punto un montón de métodos con operaciones que puedes hacer sobre tu array para obtener información sobre el mismo o para modificar los elementos que hay en su interior.

Ya no solamente length: hoy en día hay muchísimas funciones. En particular dentro de este array, dos de las funciones en las que me quiero centrar hoy son forEach y map, aunque, en verdad, este consejo que os voy a dar en este tutorial os vale para muchísimas de estas funciones porque se comportan de forma parecida. Muchas de estas funciones aceptan como parámetro una función, que es algo que a estas alturas puede que ya sepas que se puede hacer en JavaScript.

La función va a ser un elemento de primera clase, así que puedes crear una función anónima, sin nombre ni nada, y pasarla como parámetro a las funciones. Puede que a estas alturas sepa lo que es un arrow function y puedas expresarlo de una forma más corta, pero yo por compatibilidad y para no asustar a nadie voy a poner function. Esta función no solamente va a estar ahí, sino que también va a recibir como parámetro un elemento, al cual voy a llamar, por ejemplo, verd, de verdura. Y es que lo que va a hacer forEach es, atención a esto, llamar a la función que le pasemos como parámetro tantas veces como elementos tenga el array al que pertenece la función. En este caso, llamará a mi función de callback seis veces.. perdón, cinco, que no se contar. En cada una de las iteraciones que se haga sobre mi callback le va a ir pasando a la variable verd cada uno de los elementos del array. Es decir, que cuando hago verduras.forEach, el forEach tomará la función que yo le estoy pasando, y la llamará una vez con el elemento patata, una vez con el elemento puerro, una vez con el elemento boniato, una vez con el elemento coliflor, y otra vez con el elemento tomate. Cinco ejecuciones del mismo callback usando en cada caso una variable diferente.

Si no os lo creéis, voy a hacer una cosa: voy a poner console.log y voy a ver qué vale la variable verd. Cuando ejecute mi programa, por ejemplo con node main en la consola, puedo ver que mi función está siendo llamada cinco veces porque aparece ese console.log un total de cinco veces, y en cada caso la variable verd vale una cosa diferente.

A lo mejor alguien se está preguntando qué diferencia hay entre este forEach y un bucle for de toda la vida. Desde hace 30 años, en JavaScript ha sido posible recorrer cada uno de los elementos de un array utilizando esta construcción: yo itero desde el elemento 0 hasta el elemento máximo que haya dentro del array, y para cada una de las iteraciones extraigo el elemento que hay en la posición i del array. Si pongo un console.log, veremos que funciona igual. Se imprime el mismo array, porque realmente se comportan exactamente igual. Luego, antes de avanzar estaría bien saber por qué hay dos formas de hacer lo mismo, y qué diferencia hay entre hacer un for tradicional y usar laf unción forEach.

El bucle for de toda la vida es imperativo. Nosotros le estamos diciendo al ordenador qué es lo que tiene que hacer: vas a coger el elemento i, vas a ver qué hay dentro del elemento i, luego vas a incrementar la variable i, y en eso consiste la programación imperativa, en decirle cómo tiene que comportarse.

En cambio, forEach forma parte de un paradigma de programación que se usa bastante hoy en día, llamado programación declarativa. En programación declarativa nosotros le pedimos al ordenador que haga cosas, pero nos fiamos de su capacidad y de su conocimiento para que haga las cosas por nosotros. Yo ya no tengo que decirle que itere por cada elemento y que saque lo que hay en la posición i, solamente le pido que itere sobre cada uno de los elementos del array verduras. Esto es lo que hace la función forEach. Ahora, ¿qué hay en la función? Ni lo sé, ni me importa.

Podéis hacer un experimento: fabricaros vuestra propia función forEach y hacerlo con un bucle for tradicional. Es posible que dentro de la función forEach, esté implementado así. La única garantía que me da forEach, y ya voy a quitar el for anterior, es que si os fijáis, los elementos se imprimen en el mismo orden en el cual está en el array. Es decir, forEach no solamente llama al callback una vez por elemento, sino que lo hace en el mismo orden. Lo cual es todo un detalle, porque imagina que llama a los elementos en el orden que le apetece.

En principio, podría darme igual, pero puede que no me dé igual. La programación declarativa está relacionada con la declaración funcional. Es un paradigma de programación alternativo que hoy en día está bastante de moda. Muchos de los fans de la programación funcional se pueden tirar horas argumentando sobre por qué es el mejor paradigma que hay hoy en día, así que seguramente pongan el grito en el cielo o pongáis el grito en el cielo, si es que os gusta la programación funcional, si veis lo que voy a hacer ahora mismo, que es la razón por la cual existe map.

Imaginemos que estoy haciendo un programa de ordenador para una tienda. Estos son los productos que mi cliente me ha comprado. Y como esta es una tienda un poco rara, el precio que tiene cada verdura se calcula usando una fórmula matemática. El kilo de verdura de lo que sea que estés comprando te lo voy a cobrar a 2 € por el número de letras que tiene ese producto en concreto. Si estás comprando una patata, te va a valer a 12 €, porque es 2 * 6, y si estás comprando una coliflor, va a valer 14 € el kilo, porque eson 7 * 2. Hay que ver lo terrible que está la inflación.

Por ejemplo, imagina que yo utilizo una variable llamada precio para estimar ese precio que te voy a cobrar. Lo que hago es sacar el número de caracteres de mi cadena verd y multiplicarlo por 2. Ahora voy a hacer un console.log para imprimir cada precio que le voy a cobrar a esta persona. Aquí los vemos: 12, 12, 14, 16, y 12. De momento, solo estoy iterando por cada uno de los elementos del array, y esto sigue siendo perfectamente legal y válido, pero ahora vamos a darle un pequeño girito. Imaginemos que para hacer funcionar correctamente mi programa, tengo que generar un ticket de compra con el precio de cada una de las cosas que estoy comprando, es decir, que necesito tener por separado cada uno de esos precios para poder sumarlo.

Una persona un poco ingenua podría decir como en JavaScript tenemos el concepto de scope, si declaro fuera del forEach una variable llamada precios, puedo usar la función precios.push, que se usa para insertar elementos en el array, para introducirle el precio a esa variable precios. Y eso, técnicamente hablando estaría bien. Es decir, si después de cada bucle for yo imprimo lo que vale la variable precios, me voy a encontrar con que vale un array con todos los precios que le voy a cobrar por su compra completa. Sin embargo, a cualquier persona que le guste la programación funcional va a poner el grito en el cielo, porque va a decir que esta función que he hecho ahora mismo es impura, es decir, que tengo una función que hace efectos colaterales a medida que se ejecuta. Estoy tocando una variable que no es global pero que se le parece, porque no forma parte del contexto de la función. Es algo que, en general, en programación deberíamos evitar. Por una razón muy simple: para cualquier persona que lea el código fuente del programa, le va a costar un poco más entender qué es exactamente lo que hace esta función porque está tocando variables de vete a saber dónde, y es algo que si queremos escribir funciones más claras y sencillas de comprender tanto por nosotros como por la máquina, es algo que deberíamos evitar, para evitar posibles errores.

Pero este es precisamente el tipo de caso de uso por el cual se introdujo la función map. Map es una función muy parecida a forEach. Como dije antes, hay muchas funciones que reciben como parámetro un callback, y map es una de ellas. Map va a recibir como parámetro otro callback similar al que recibe la función forEach, con su parámetro verd, que va a valer cada uno de los elementos del array procesándose uno tras otro. Por ejemplo, si hago console.log dentro de mi función map y trato de ejecutar mi script, vamos a ver igualmente las variables en el mismo orden. Hasta aquí no hay nada diferente. Ahora bien, map va un paso más allá de la función forEach. El callback de la función map tiene que devolver una cosa, y lo que hará map es bastante simple aunque tal vez al principio un poco complicado de entender. Map se va a fabricar por su cuenta un array que tiene tantos elementos como elementos hay en el array al cual llamamos a map. Como verduras tiene cinco elementos, map va a devolver un array de 5 elementos también, que podemos interceptar asignándoselo a la variable result, en este caso. Cuando map llama a mi callback utilizando el elemento que está en la posición i, lo que hace es preguntarse qué es lo que devuelve mi callback para ese elemento en la posición i, y lo que devuelva este callback lo va a poner en la posición i del array de destino.

Esto quiere decir que si yo el primer elemento es patata, voy a llamar a esta función con verd valiendo patata, y lo que me devuelva para patata lo voy a poner en la posición 0 del array result. Lo que devuelve cuando le pasas el elemento 0 va a a la posición 0 de result. Lo que devuelve cuando lo haces con el 1, va a la posición 1. Si lo haces con la 2, va a la 2. Así con todas. Es decir, result va a ser un array de 5 elementos donde el elemento 0 es el return para el elemento 0 de verduras, y el elemento 1 de result va a ser lo que devuelva para el elemento 1 de verduras. Y así con todos, manteniendo el orden. En esto consiste el mapeo: es una transformación. Tienes un array con elementos que siguen un órden, y lo que vas a hacer es transformar el array transformando cada uno de los elementos del array.

La otra cosa importante sobre map es que es una función pura si la tratas bien, porque no te modifica verduras en ningún momento. Te va a devolver una copia, pero verduras se va a quedar igual que estaba antes. No es como push, que va tocando el array original. Si en este return le pongo, precisamente, la fórmula del precio de un producto, lo que obtengo es una forma más pura de calcular el array de precios que estoy pagando por mi compra, porque para el elemento 0 este callback va a devolver 12, así que precios[0] va a valer 12. Para el elemento 2, precios[2] va a ser 14, y para el elemento 4, precios[4] va a ser 16. Porque es lo que va devolviendo mi función map, o mejor dicho, su callback, con cada uno de los elementos que hay en el array original.

Si imprimo ese array después de procesar los elementos de mi mapa, nos vamos a encontrar otra vez con el mismo array, pero esta vez de una forma más pura, sin haber utilizado variables globales ni haber modificado cosas que estén fuera del contexto. forEach, en cambio, no devuelve nada. Aunque pongas un return, ese return se va a descartar. De hecho, forEach devuelve void, es decir, no se le asigna nada, es undefined, no hay nada que devolver.

¿Se puede usar map? Se puede usar map, evidentemente. Hay gente que, por alguna razón llama a map, y lo utiliza como un forEach, es decir, haciendo csoas en cada uno de los elementos, pero nunca devuelve nada. Tampoco asigna el resultado de map a ninguna variable. Si bien se va a ejecutar para cada uno de los elementos igualmente, es un comportamiento que deberíais evitar, más que nada, por cualquier persona que esté leyendo el código. Si yo me encuentro un forEach voy a decir ah, voy a iterar sobre cada uno de los elementos para comprobar algo, pero si me encuentro un map, voy a decir ojo, esta persona lo que quiere es transformar cada elemento de alguna forma, luego, si un map no me devuelve nada, va a ser un poco raro.

Para todas aquellas personas que se estén preguntando si esto es una regresión respecto al for de toda la vida; yo en mi for de toda la vida, puedo saber qué posición del array es la que estoy procesando. En realidad, casi todos los callbacks que se le pueden pasar a las funciones de array, aceptarán más de un parámetro. Pasa que yo lo estoy descartando, pero si yo en mis callbacks le pongo un segundo parámetro, por ejemplo, el i, este parámetro se va a rellenar en cada ejecución del callback. Concretamente con la posición que tiene el elemento verd dentro del array original. Dentro de cada elemento que se le pase a esta función, i primero valdrá 0, luego valdrá 1, luego valdrá 2, luego 3 y luego 4. Si no os fiáis de mi palabra, le voy a poner una i en dos console.logs, y veréis que cuando ejecuto el programa aparece 01234-01234. Así que si quieren trabajar con índices, se puede hacer, evidentemente.

Eso sí, una cosa que nunca, nunca, nunca, nunca deberíais hacer, es utilizar esta i para modificar el propio array al cual estáis llamado en forEach o map dentro del mismo callback. Por ejemplo, no tendría mucho sentido que dentro de un map modificase el elemento que tengo a mi derecha o el que tengo a mi izquierda. Y de hecho ahora mismo no sé ni si quiera si funcionaría o no, pero da igual. Tampoco me interesa saberlo, porque incluso si funciona, es una idea terrible. Pensad en la pobre persona que está leyendo el código fuente e intente comprender qué es lo que hace vuestro callback. Si se encuentra que el callback está modificando variables de por ahí fuera, incluido el mismo array, va a ser muy complicado de comprender qué está haciendo este array, porque no es una función matemática pura que transforma cada elemetno del array, sino que encima puede pasar literalmente cualquier otra cosa. No toquéis la variable sobre la que estáis iterando dentro de una de estas funciones.

Si queréis ejercicios avanzados para practicar forEach y map y comprenderlo bien, una idea que se me ocurre es que hagáis vuestra propia implementación de forEach y map, aunque sea usando el bucle for.

Si os ha gustado este vídeo, os invito que pulséis el botón Me gusta, así me lo señalizará para sepa que tengo que hacer más contenido como este. Si no estás suscrito a este canal de YouTube, considera hacerlo, para así enterarte de consejos como este cada vez que suba un vídeo. Nada más, nos vemos pronto, que tengas un día estupendo y hasta luego.