Método Map para JavaScript y React
Tags: javascript, typescript, react
Published: 2021-09-11 21:17:11

El método map transforma los elementos del array uno por uno aplicándoles una función en base al elemento y a su posición, dicha función es programada por nosotros de acuerdo a la necesidad es decir podemos usar condicionales, y distintas herramientas que la programación nos permita ejecutar siempre y cuando se ejecuten de forma síncrona 


Cuando se usa el método map, la función que podemos llamar función transformadora  toma un argumento requerido y dos opcionales:


1. El valor actual del elemento - requerido
2. El índice del elemento - opcional
3. Toda la matriz - opcional


```javascript
.map( function callback( element, index, arr) {

});
```
Map vs ForEach

```javascript
const numbers = [1,2,3];
```

El método 'map' devuelve un nuevo arreglo, aplicando una operación en cada uno de los elementos, pero no modifica el arreglo original 


```javascript
const doubleNumbers = numbers.map( n => n * 2 ) ;
// doubleNumbers = [2,4,6]
// numbers = [1,2,3]
```

El método forEach en cambio no devuelve ningún valor, directamente como lo hace map y si operamos sobre el modificamos los valores del arreglo original 

```javascript
const doubleNumbers = numbers.forEach( n => n * 2 ) ;
// doubleNumbers = undefined;

numbers.forEach((item, index) => {
    array[index] = item * 2; // ahora si mutamos el array original.
}); 
//  numbers = [2,4,6]
```
Gracias a @lukeshiru por la aclaración en este punto


Cosas que no podemos hacer con 'map'

No podemos ejecutar una función asíncrona dentro de map y esperar un resultado
async - await 🚫

🚫

```javascript
const doubleNumbers = numbers.map( async function (n) { 
const res = await fetch(`api/double/${n}`);
.....
})
```
🚫




Avancemos con más ejemplos del uso de map



Ejemplo Práctico 1 

Escribir una función que de reciba un arreglo con los planetas: Earth, Saturn, Pluto, Jupiter y devuelva [5,6,5,7]

```javascript
let foo () = () => {
let planets = [ 'Earth', 'Saturn', 'Pluto', 'Jupiter'];

return planets.map( planet => planet.length)

}

```
En este caso hemos usado la función map para retornar un nuevo array con la cantidad de caracteres de cada uno de los planetas



Ejemplo Práctico 2

Cuantos años pasaron desde el año actual a cada uno de los años en una lista parece un trabando lengua pero en ocasiones tenemos este tipo de requerimientos donde tenemos que iterar elementos y queremos aplicar la misma acción a cada uno, la solución habitual es usar un for de toda la vida, pero javascript nos provee métodos más eficientes, limpios que se derivan de la programación funcional a continuación el ejemplo


```javascript
const since = [2000, 2001, 2010, 2020];


const yearPassed = [];
for (let i = 0 ; i < since.length ; i++){
   const sinceYear = since[i];
    yearPassed.push( 2021 - sinceYear );
}

```

En esta ocasión nos vemos obligados a recorrer el arreglo con for, e introducir los nuevos elementos con el método 'push' en nuevo arreglo creado también previamente generando así varias lineas de código, cuando pudimos resolver el problema incluso en una sola linea con el método 'map'

```javascript
const yearPassed = since.map( year => 2021 - year ) ;
```


Ejemplo Práctico 3

En este ejemplo tenemos un arreglo de objetos con información de autos y deseamos aplicar un descuento a los que tenga un precio menor a 15.000 

```javascript
const cars = [ 
{ id: 'PSS-123', model: 'Mustang', price: 30000},
{ id: 'CHS-345', model: 'Camaro', price: 14500},
{ id: 'ABS-567', model: 'Aveo', price: 9000},
];


const carsDiscount = cars.map( function(car) { 

 if( car.price < 15000 ) 
return { 
...car,
price: car.price *0.9 
}

})
```

De esta forma evaluamos la condición y luego en el retornamos un nuevo objeto donde copiamos las propiedades el objeto en original en este caso carro,  pero sobrescribimos su propiedad 'price'

También puede que se nos solicite extraer todo los id de los carros para lo que podemos usar map nuevamente

```javascript
const idCars = cars.map( car => car.id);
```

Podemos optimizar este código  desarmado el arreglo para extraer lo que necesitamos con la desestructuración la propiedad debido a que estamos repitiendo la variable car entonces el código se ve así

```javascript
const idCars = cars.map( ( { id } ) => id );
```

entonces con la ayuda de las llaves hemos extraído unicamente id y es lo que estamos retornando en el nuevo arreglo

Map en React 

El uso común de la función 'map' en React es para iterar elementos del DOM, por ejemplos representar una lista de elementos, en este caso una lista usuarios y tomar sus nombres

```javascript
export default function App() {

  const data = [
    { id: 1, name: "John Doe" },
    { id: 2, name: "Victor Wayne" },
    { id: 3, name: "Jane Doe" },
  ];

  return (
    <div className="App">
           {data.map((user) => (
        <div className="user" key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}
```

Esta código de React retorna elementos del DOM con el atributo name de los usuarios, la propiedad id es tomada por el atributo especial de react "key" que es usado para tener referencia del elemento que se esta iterando y saber si los elementos han cambiado, se han agregado o se han eliminado

Ejemplo Práctico 4 

En este ejemplo se usa map de dos formas para operar sobre elementos de un arreglo y para renderizar la lista resultando en DOM - El requermiento es tomar una lista de informacion sobre images, poner su nombre con la primera letra en mayuculas y luego colocar en una sola linea de texto su ancho y largo

```javascript
export default function App() {

  const stringifyImageSizes = (imageSizes) => {
    return imageSizes.map((a) => {
      const capitalizedName = a.name[0].toUpperCase() + a.name.slice(1);
      return `${capitalizedName} image - ${a.width} x ${a.height}`;
    });
  }

  const imageSizes = [
    { name: "horizontal", width: 600, height: 380 },
    { name: "vertical", width: 400, height: 650 },
    { name: "thumbnail", width: 300, height: 300 },
  ];

  const normalizedImageStrings = stringifyImageSizes(imageSizes);

  return (
    <div className="images">
      {normalizedImageStrings.map((s) => (
        <div className="image-type">{s}</div>
      ))}
    </div>
  );
}
```


Finalmente concluiremos con dos ejemplo en TypeScript aplicando varias conceptos de este lenguaje y demostrando que el método 'map' funciona de la misma forma pues sigue siendo javascript bajo el capo, de esta forma tenemos el siguiente requerimiento: Crear una función para marcar como Completo toda una lista de 'cosas por hacer'  la tarea por hacer tiene la siguiente estructura

```javascript
type Todo = Readonly<{
  id: number
  text: string
  done: boolean
}>
```


Para marcarla completo deberíamos cambiar la propiedad 'done' a true, para lo cual podríamos crear otro tipo CompletedTodo copiando todas las propiedades anteriores y definiendo 'done': true pero estaríamos repitiendo código innecesariamente debido a que TypeScript provee una funcionalidad llamada 'intersección de tipos'  usando el operador '&' entonces el tipado a crear, quedaria de la siguiente forma: 

```javascript
// Sobreescribir la propiedad donde en el Todo
type CompletedTodo = Todo & {
  readonly done: true
}
```

Con este tipado obligamos a que mientras escribamos la función a realizar la propiedad done cumpla tanto con todo lo de type 'Todo' así como que la propiedad done: true y si por ejemplo colocamos done: false TypeScript avisará inmediatamente del error

Y luego crearíamos la función necesaria para marcar todos como completos usando el tipado necesario, así como el recorrido por los 'todos' que están en el arreglo usando la función 'map' copiando las propiedades de los otros 'todo' con '...todo' y finalmente se cambian la propiedad 'done' a true

```javascript
function completeAll(
  todos: readonly Todo[]
): CompletedTodo[] {
  return todos.map(todo => ({
    ...todo,
    done: true
  }))
}
```

Por último un ejemplo donde se usa React + Typescript y le método Map para crear una tarjetas de tareas usando también estilos de Bootstrap 


```javascript
              {tasks.map((t: ITask, i: number) => (
                <div key={i} className="card card-body mt-2">
                  <h2 style={{ textDecoration: t.done ? "line-through" : "" }}>
                    {t.name}
                  </h2>
                  <div>
                    <button
                      onClick={() => toggleDoneTask(i)}
                      className="btn btn-secondary"
                    >
                      {t.done ? "✓" : "✗"}
                    </button>
                    <button
                      onClick={() => removeTask(i)}
                      className="btn btn-danger"
                    >
                      🗑
                    </button>
                  </div>
                </div>
              ))}
``` 


De esta forma observamos como el uso de 'map' puede evolucionar desde iterar solo elementos como números a componentes del DOM y usando un tipado de estricto de TypeScript para crear aplicaciones más robustas y terminar vistas mejorar estructuradas


Para crear este post me he basado en distintas fuentes a las cuales agradezco a sus autores 

 [React Doc Oficial](https://es.reactjs.org/docs/lists-and-keys.html) 
 [Chibicode](https://ts.chibicode.com/todo/) 
 [Fazt Code TypeScript](https://www.youtube.com/watch?v=IbJFERe9F9w) 
 [La Cocina del Código Youtube](https://www.youtube.com/watch?v=tP8JiVUiyDo)