Encontrar su camino con .Map ()

La verbosidad y la elegancia de una solución están impulsadas por las herramientas que tenemos para resolver un problema en particular. Si bien el objetivo de la resolución de problemas es resolver un problema , sus métodos deben avanzar hacia la forma más elegante posible. El viaje hacia esa solución, sin embargo, parece estar en una curva asintótica. La perfección se acerca cada vez más, pero siempre permanece fuera de su alcance.

El problema

Imagínese tener una matriz y necesitar cambiar cada elemento de la matriz. Quizás, por ejemplo, tomando una serie de alturas en pulgadas y necesitando convertirlas en centímetros. O posiblemente convertir una serie de temperaturas en grados Celsius a Fahrenheit. Si es nuevo en la programación, su mente puede ir inmediatamente a algún tipo de bucle. ¿Y adivina qué? Estoy seguro de que podrías hacerlo funcionar.

Sin embargo, estoy aquí para darle una herramienta más - algo para conseguir que sólo un poco más cerca de elegante: Array.prototype.map().

El mapmétodo nos permite transformar cada elemento de una matriz, sin afectar la matriz original. Se considera una función de orden superior y una técnica de programación funcional porque toma una función como argumento y estamos realizando cálculos sin mutar el estado de nuestra aplicación.

Mapes una propiedad que se hereda del prototipo de matriz. Los prototipos proporcionan métodos integrados con los que vienen los objetos (las matrices son tipos especiales de objetos a los ojos de JavaScript). Si bien mappuede ser un poco más extraño, este prototipo no es diferente, por ejemplo, del Array.lengthprototipo. Estos son simplemente métodos que están integrados en JavaScript. Los prototipos de matrices se pueden agregar y mutar mediante: Array.prototype.= ...

Al final de esta lección, descubriremos cómo mapfunciona y escribiremos nuestro propio método de prototipo de matriz.

Entonces, ¿qué hace .map ()?

Supongamos que tiene una variedad de temperaturas en grados Celsius que desea convertir a Fahrenheit.

Hay varias formas de resolver este problema. Una forma puede ser escribir un forciclo para crear una matriz de temperaturas Fahrenheit a partir de las temperaturas Celsius dadas.

Con el forbucle podríamos escribir:

const celciusTemps = [22, 36, 71, 54]; const getFahrenheitTemps = (function(temp) { const fahrenheitTemps = []; for (let i = 0; i < celciusTemps.length; i += 1) { temp = celciusTemps[i] * (9/5) + 32 fahrenheitTemps.push(temp); } console.log(fahrenheitTemps); [71.6, 96.8, 159.8, 129.2 })();

Un par de cosas a tener en cuenta:

  1. Funciona.
  2. Usamos una expresión de función inmediatamente invocada (IIFE) para evitar tener que llamar también a la función.
  3. Es un poco prolijo y no muy elegante.

Map nos permite tomar el código anterior y refactorizarlo a lo siguiente:

const fahrenheitTemps = celciusTemps.map(e => e * (9/5) + 32); console.log(fahrenheitTemps); // [71.6, 96.8, 159.8, 129.2]

Entonces, ¿cómo funciona el mapa?

Maptoma una función y aplica esa función a cada elemento de la matriz. Podríamos escribir mapun poco más detallado con ES5 para ver esto un poco más claramente.

const fahrenheitTemps = celciusTemps .map(function(elementOfArray) { return elementOfArray * (9/5) + 32; }); console.log(fahrenheitTemps); // [71.6, 96.8, 159.8, 129.2]

Si nuestra función de mapa pudiera decir lo que está haciendo, diría:

"Para cada elemento en la matriz, lo multiplico por (9/5), luego sumo 32. Cuando eso está hecho, devuelvo el resultado como un elemento en una nueva matriz llamada fahrenheitTemps".

Veamos un caso de uso más común. Supongamos que tenemos una matriz de peopleobjetos. Cada objeto tiene una namey ageclave-valor de par. Queremos crear una variable que sea solo los nombres de todos en la matriz. Con nuestro formétodo de bucle podríamos escribir:

const people = [ {name: Steve, age: 32}, {name: Mary, age: 28}, {name: Bill, age: 41}, ]; const getNames = (function(person) { const names = []; for (let i = 0; i < people.length; i += 1) { name = people[i].name; names.push(name); } console.log(names); // [Steve, Mary, Bill]; })();

Con map:

const names = people.map(e => e.name); console.log(names) // [Steve, Mary, Bill];

Observe que aquí no transformamos nada, simplemente devolvemos el par clave-valor name.

Nuevamente, los forbucles funcionan. Pero es muy detallado y tenemos que crear una nueva función personalizada cada vez que queremos hacer una transformación diferente. Una parte principal de la programación es escribir código DRY (Don't Repeat Yourself). Estas funciones de orden superior, como map, nos permiten hacer una programación más compleja en menos líneas de código de lo que podríamos sin ellas.

Reinventando la rueda:

Para comprender mejor lo que está sucediendo bajo el capó, crearemos nuestra propia función de mapa que adjuntaremos al prototipo de matriz.

Primero, para adjuntar un método prototipo a una matriz, escribiremos:

Array.prototype.

entonces para nosotros:

Array.prototype.myMap =

Pero, ¿cuál será nuestro código?

Ya tenemos la lógica que necesitamos de los forbucles anteriores. Todo lo que tenemos que hacer es refactorizarlo un poco. Refactoricemos la última función que escribimos getNames().

Recuerde, esta función tomó una persona (en otras palabras, un elemento de nuestra matriz), hizo una transformación personalizada a ese elemento (con el forciclo y algo de lógica) y devolvió una matriz de nombres (o una nueva matriz).

const getNames = (function(person) { const names = []; for (let i = 0; i < people.length; i += 1) { name = people[i].name; names.push(name); } console.log(names); // [Steve, Mary, Bill]; })();

Primero, cambiemos el nombre de nuestra función. Después de todo, este nuevo método no asume saber sobre qué tipo de matriz actuará:

const myMap = (function(person) { //Changed name const names = []; for (let i = 0; i < people.length; i += 1) { name = people[i].name; names.push(name); } console.log(names); // [Steve, Mary, Bill]; })();

En segundo lugar, estamos creando nuestra propia versión de .map(). Sabemos que esto tomará una función que proporcione el usuario. Cambiemos el parámetro que toma nuestra función:

// It is a bit verbose, but a very clear parameter name const myMap = (function(userProvidedFunction) { const names = []; for (let i = 0; i < people.length; i += 1) { name = people[i].name; names.push(name); } console.log(names); // [Steve, Mary, Bill]; })();

Finalmente, no tenemos idea de en qué matriz actuará este método. Por lo tanto, no podemos referirnos a people.lengthpero nos podemos referir a this.length. this, devolverá la matriz sobre la que actúa el método. Además, limpiemos algunos de los otros nombres de variables:

const myMap = (function(userProvidedFunction) { // change variable name const newArr = []; // use "this.length" for (let i = 0; i < this.length; i += 1) { // use "this[i]", and change variable name const newElement = this[i]; // update the array we push into newArr.push(newElement); } // Return the newly created array return newArr; })();

We’re almost there, but there is one thing we are forgetting. We haven’t transformed the array! All we’ve done above is return the old array. We have to apply the user-provided function to each element of the array:

const myMap = (function(userProvidedFunction) { const newArr = []; for (let i = 0; i < this.length; i += 1) { /* Transform the element by passing it into the * user-provided function */ const newElement = userProvidedFunction(this[i]); newArr.push(newElement); } return newArr; })();

Finally, we can attach our new function toArray.prototype.

Array.prototype.myMap = myMap;

A final sanity check:

const myArray = [1, 2, 3]; // Multiply each element x 2 const myMappedArray = myArray.myMap(e => e * 2) console.log(myMappedArray) // [2, 4, 6];

Summary

Map is a prototype method offered by arrays. Behind the scenes, it iterates through the array, applying a user-provided function to each element. Ultimately, it returns a new array with the transformed values. It does this without mutating the original array. Because the parameter it takes is a function, it is considered a higher-order function. In addition, its use falls into the functional programming paradigm.

Thanks for reading!

woz