DEV Community

Cover image for Método Map para JavaScript y React
kevingo710
kevingo710

Posted on • Updated on

Método Map para JavaScript y React

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
.map( function callback( element, index, arr) {

});
Enter fullscreen mode Exit fullscreen mode

Map vs ForEach

const numbers = [1,2,3];
Enter fullscreen mode Exit fullscreen mode

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

const doubleNumbers = numbers.map( n => n * 2 ) ;
// doubleNumbers = [2,4,6]
// numbers = [1,2,3]
Enter fullscreen mode Exit fullscreen mode

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

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]
Enter fullscreen mode Exit fullscreen mode

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 🚫

🚫

const doubleNumbers = numbers.map( async function (n) { 
const res = await fetch(`api/double/${n}`);
.....
})
Enter fullscreen mode Exit fullscreen mode

🚫

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]

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

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

}

Enter fullscreen mode Exit fullscreen mode

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

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


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

Enter fullscreen mode Exit fullscreen mode

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'

const yearPassed = since.map( year => 2021 - year ) ;
Enter fullscreen mode Exit fullscreen mode

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

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 
}

})
Enter fullscreen mode Exit fullscreen mode

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

const idCars = cars.map( car => car.id);
Enter fullscreen mode Exit fullscreen mode

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í

const idCars = cars.map( ( { id } ) => id );
Enter fullscreen mode Exit fullscreen mode

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

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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

type Todo = Readonly<{
  id: number
  text: string
  done: boolean
}>
Enter fullscreen mode Exit fullscreen mode

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:

// Sobreescribir la propiedad donde en el Todo
type CompletedTodo = Todo & {
  readonly done: true
}
Enter fullscreen mode Exit fullscreen mode

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

function completeAll(
  todos: readonly Todo[]
): CompletedTodo[] {
  return todos.map(todo => ({
    ...todo,
    done: true
  }))
}
Enter fullscreen mode Exit fullscreen mode

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

              {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>
              ))}
Enter fullscreen mode Exit fullscreen mode

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
Chibicode
Fazt Code TypeScript
La Cocina del Código Youtube

Discussion (2)

Collapse
lukeshiru profile image
LUKESHIRU

El ejemplo comparando Array.prototype.map con Array.prototype.forEach esta herrado. forEach no modifica el array original a menos que lo hagas de forma explicita:

const array = [1, 2, 3];
array.forEach(item => item * 2); // Devuelve undefined, array sigue siendo [1, 2, 3]
array.forEach((item, index) => {
    array[index] = item * 2; // ahora si mutamos el array original.
});
Enter fullscreen mode Exit fullscreen mode

Si bien deberías evitar mutar el array original, hago esta este ejemplo para mostrar porque forEach no modifica el array original a menos que lo hagamos de forma explicita.

También me gustaría agregar que si bien no podes usar async/await con map, si podes usar Promise.all. Siguiendo tu ejemplo:

Promise.all(numbers.map(number => fetch(`api/double/${number}`))).then(
    responses => {
        // Ahora podemos hacer lo que queramos con el array de respuestas
    }
);

// Oh si preferís usar await:

const responses = await Promise.all(
    numbers.map(number => fetch(`api/double/${number}`))
);
// Ahora podemos hacer lo que queramos con el array de respuestas
Enter fullscreen mode Exit fullscreen mode

Saludos!

Collapse
kevingo710 profile image
kevingo710 Author

Gracias por tus comentarios y ejemplos tomo tu corrección en la comparación de map y forEach para mejorar y aclarar el ejemplo, y perfecto podemos envolver el map en una Promise para usar async/await