Cómo usar el método .map() en JavaScript para modificar dinámicamente una matriz de objetos.
Primeros pasos
Debido a que el código que estamos escribiendo para este tutorial es "independiente" (lo que significa que no es parte de una aplicación o proyecto más grande), vamos a crear un proyecto de Node.js desde cero. Si aún no tiene Node.js instalado en su computadora, lea este tutorial primero y luego regrese aquí.
Una vez que haya instalado Node.js en su computadora, desde la carpeta de proyectos en su computadora (por ejemplo, ~/projects
), crea una nueva carpeta para nuestro trabajo:
Terminal
mkdir map
A continuación, cd
en ese directorio y crea un index.js
archivo (aquí es donde escribiremos nuestro código para el tutorial):
Terminal
cd map && touch index.js
A continuación, en la misma carpeta, ejecute npm init -f
para arrancar un package.json
archivo:
Terminal
npm init -f
Esto le indicará a NPM (Node Package Manager) que cree un nuevo package.json
archivo en su aplicación. El -f
parte significa "fuerza" y omitirá el asistente paso a paso que ve cuando ejecuta npm init
por sí mismo (siéntete libre de usar esto para entender cómo funciona).
Finalmente, necesitamos instalar dos dependencias:dayjs
y currency.js
.
Terminal
npm i dayjs currency.js
Usaremos estos dos para analizar y formatear nuestros datos como parte de nuestro .map()
.
Con eso, estamos listos para comenzar.
Adición de datos de usuario
Nuestro objetivo para este tutorial es usar el Array.map()
en JavaScript para dar formato a algunos datos de usuario y ayudarnos a comprender quiénes son nuestros usuarios más valiosos. Para comenzar, agreguemos algunos datos de prueba en un archivo separado en la raíz de nuestro proyecto:
/usuarios.js
export default [
{
"createdAt": "2021-12-08T16:20:14+00:00",
"invoices": [
{
"createdAt": "2021-12-08T16:20:14+00:00",
"amount": 790.31
},
{
"createdAt": "2021-12-07T16:20:14+00:00",
"amount": 893.38
},
{
"createdAt": "2021-12-06T16:20:14+00:00",
"amount": 302.97
},
...
],
"name": {
"first": "Wester",
"last": "Christian"
},
"emailAddress": "[email protected]"
},
...
];
Nota:esta es una lista abreviada ya que la lista real (disponible aquí en Github) es bastante larga.
Una vez que tenga eso en la aplicación, estamos listos para pasar a escribir nuestro .map()
sobre esta matriz.
Mapeo sobre la matriz de usuarios
Para empezar, construyamos un esqueleto para nuestro .map()
función y revisar y discutir cómo va a funcionar:
/index.js
import users from './users.js';
const spendByUser = users.map((user) => {
// We'll return our modified user here...
});
console.log(spendByUser);
De vuelta en nuestro /index.js
archivo, aquí, importamos nuestro /users.js
archivo como users
(recuerde, tenemos un export default
en ese archivo para que podamos decir import users
en nuestro código:si se tratara de una exportación con nombre, veríamos algo como import { users } from '...'
).
Porque sabemos que users
La variable debe contener una matriz (lo que exportamos de /users.js
), podemos llamar a .map()
directamente sobre él. Esto se debe a que .map()
es una función integrada en JavaScript. Está definido en el Array
prototipo (el nombre utilizado para el objeto que contiene la funcionalidad heredada por una función en JavaScript). Decimos "A" mayúscula Array
aquí porque esa es la función en JavaScript que define el comportamiento de una matriz. Como parte de su objeto prototipo, tenemos el .map()
función (conocida como método porque es una función definida en un objeto existente).
Como sus hermanos, .map()
nos permite realizar un bucle sobre una matriz y hacer algo . El algo en este caso es modificar elementos en un arreglo y devolverlos, creando un nuevo arreglo con los elementos modificados. Un ejemplo rápido:
const greetings = ['Hello', 'Goodbye', 'See ya'];
const greetingsWithName = greetings.map((greeting) => {
return `${greeting}, Ryan!`
});
console.log(greetingsWithName);
// ['Hello, Ryan!', 'Goodbye, Ryan!', 'See ya, Ryan!']
Aquí tomamos una matriz de cadenas y usamos .map()
para recorrer esa matriz. Para cada cadena en la matriz, devolvemos una nueva cadena (creada usando acentos graves para que podamos aprovechar la interpolación de cadenas de JavaScript). En respuesta a nuestra llamada a greetings.map()
obtenemos una nueva matriz. Es importante entender :esta es una matriz nueva y única. El .map()
la función crea una copia de cualquier matriz que llamemos .map()
y devuelve esa nueva matriz.
Aquí, almacenamos eso en una variable greetingsWithName
y luego console.log()
para ver la copia modificada.
/index.js
import dayjs from 'dayjs';
import users from './users.js';
const spendByUser = users.map((user) => {
return {
isLegacyCustomer: dayjs(user?.createdAt).isAfter(dayjs().subtract(60, 'days')),
name: `${user?.name?.first} ${user?.name?.last}`,
};
});
console.log(spendByUser);
Ahora que entendemos los fundamentos de .map()
, comencemos a modificar nuestro users
formación. Los mismos principios exactos están en juego como vimos anteriormente:tomamos una matriz, llamamos .map()
en él, y obtener una nueva matriz a cambio.
Arriba, observe que arriba de nuestro users
import hemos importado una de las dependencias que instalamos anteriormente:dayjs
. Abajo en nuestro .map()
función, en la devolución de llamada pasamos a .map()
, estamos devolviendo un objeto. Nuestro objetivo aquí es hacer un "análisis" de cada usuario y averiguar cuánto ha gastado cada cliente y si es o no un cliente heredado.
Aviso:no tenemos que devolver exactamente la misma forma de objeto (o incluso un objeto) desde nuestro .map()
. Solo tenemos que devolver lo que queramos que tenga lugar en el elemento actual que estamos mapeando en la matriz.
Aquí, queremos crear un nuevo objeto que tenga tres propiedades:
isLegacyCustomer
que nos dice como booleanotrue
ofalse
independientemente de si el cliente se considera heredado o no.name
que es la cadena de nombre completo del usuario/cliente.spend
que es la cantidad de dinero que han gastado con nosotros, compuesta por un total de susinvoices
matriz.
Aquí, nos estamos enfocando solo en isLegacyCustomer
y name
(spend
es un poco más complicado, así que lo agregaremos a continuación).
Para isLegacyCustomer
, queremos saber si el usuario se creó en nuestra base de datos hace más de 60 días (solo pretendemos aquí). Para averiguarlo, tomamos el createdAt
propiedad en el user
objeto y páselo a dayjs()
(la función que importamos del paquete del mismo nombre arriba).
dayjs
es una biblioteca para manipular y trabajar con fechas. Aquí, para facilitar nuestro trabajo, usamos dayjs()
para decirnos si la marca de tiempo la pasamos (user.createdAt
) está después otra fecha que estamos creando sobre la marcha con otra llamada a dayjs
:dayjs().subtract(60, 'days')
. Aquí, obtenemos un dayjs
objeto que contiene una fecha de 60 días antes de ahora.
En respuesta, esperamos el .isAfter()
función en dayjs
para devolvernos un booleano true
o false
.
Para el name
campo, creamos una cadena usando el mismo patrón de acento grave que vimos anteriormente, esta vez usando interpolación para concatenar (unir) el nombre y apellido de nuestro usuario (usando el name.first
y name.last
propiedades del name
objeto en el usuario).
/index.js
import dayjs from 'dayjs';
import currency from 'currency.js';
import users from './users.js';
const spendByUser = users.map((user) => {
return {
isLegacyCustomer: dayjs(user?.createdAt).isAfter(dayjs().subtract(60, 'days')),
name: `${user?.name?.first} ${user?.name?.last}`,
spend: user?.invoices?.reduce((total, invoice) => {
total += invoice.amount;
return currency(total, { precision: 2 }).value;
}, 0),
};
});
console.log(spendByUser);
Ahora para la parte difícil. Para obtener el spend
propiedad para nuestros usuarios, necesitamos usar otro método de matriz .reduce()
para recorrer el user.invoices
formación. Similar a un .map()
, el .reduce()
El método recorre o itera a través de la matriz en la que se llama al método.
Sin embargo, en lugar de devolver una nueva matriz, un .reduce()
El método devuelve cualquier valor que asignemos al acc
o "acumulador". El acumulador en una función de reducción es un valor que comienza como algún valor y, si usamos .reduce()
para el propósito previsto:devuelve una versión modificada o "actualizada" de ese valor.
Aquí, el acumulador comienza como 0
pasado como segundo argumento a user?.invoices?.reduce()
(los signos de interrogación solo dicen "si el usuario existe y existen facturas en eso, llame a .reduce()
en user.invoices
"). Para cada ciclo o iteración de user.invoices
, el valor actual del acumulador (nuevamente, comenzando como eso 0
) se pasa como el primer argumento de la función que pasamos a .reduce()
, aquí etiquetado como total
. Como segundo argumento, obtenemos acceso al elemento actual en la matriz que se está repitiendo.
Si miramos nuestro código aquí, nuestro objetivo es "totalizar" el invoice.amount
campo para cada objeto en el user.invoices
matriz.
Para cada iteración de nuestro .reduce()
, tomamos el valor actual de total
y agregue el actual invoice.amount
lo. A continuación, tomamos el total
resultante y pásalo al currency()
función que importamos de currency.js
en la parte superior de nuestro archivo. Esto nos ayuda a formatear correctamente el valor de la moneda como un número flotante (por ejemplo, 123.45
). A esa función, le pasamos total
como primer argumento y luego un objeto de opciones para la función con precision: 2
como una propiedad, diciendo "formatear este número a dos lugares decimales".
Finalmente, devolvemos el .value
propiedad en el objeto devuelto por la llamada a currency(total, { precision: 2 })
. Lo que return
aquí se convierte en el valor nuevo o "actualizado" para el acumulador que estará disponible como total
en el siguiente ciclo/iteración de user?.invoices
. Así que está claro, total
en nuestro código aquí obtendrá lo siguiente para cada iteración con esta matriz de ejemplo (suponiendo que comencemos en 0
):
[{ amount: 1 }, { amount: 2.55 }, { amount: 3.50 }]
total = 0 // first item
total = 1
total = 3.55
total = 7.05 // last item
Una vez que nuestro .reduce()
completa, esperamos recuperar el valor final de total
(después de que se haya agregado el último elemento) a cambio. Esto significa que spend
debe contener el total spend
para cada uno de nuestros usuarios.
¡Eso es todo! Si le damos una vuelta a esto (asegurándonos de cerrar la sesión spendByUser
en la parte inferior de nuestro archivo), deberíamos obtener algo como esto:
[
{ isLegacyCustomer: true, name: 'Wester Christian', spend: 10729.91 },
{ isLegacyCustomer: true, name: 'Carthon Weaver', spend: 14926.53 },
{ isLegacyCustomer: true, name: 'Keldrin Durham', spend: 13491.61 },
{ isLegacyCustomer: true, name: 'Jurgen Espinosa', spend: 13179.59 },
...
]
Para terminar, echemos un vistazo a cómo hacer uso de estos datos.
Clasificación basada en datos mapeados
Entonces, ¿por qué querríamos hacer algo como esto? Como la mayoría de las cosas, depende de nuestro código y del proyecto en el que estemos trabajando. Sin embargo, para agregar contexto, podríamos suponer que estamos tratando de encontrar un cliente para recompensar en función de su gasto total con nuestra empresa. Ahora que tenemos a mano nuestra matriz mapeada, podemos hacer algo como esto:
/index.js
import dayjs from 'dayjs';
import currency from 'currency.js';
import users from './users.js';
const spendByUser = users.map((user) => { ... });
const mostValuableCustomer = spendByUser.sort((a, b) => a.spend - b.spend).pop();
console.log({ mostValuableCustomer });
Aquí, hemos añadido dos líneas. Hemos creado una variable mostValueCustomer
y para ello, estamos configurando el resultado de llamar al .sort()
método (otro Array
método prototipo) y pasándole una función. Esa función toma el elemento actual y el siguiente elemento de la matriz y los compara para encontrar cuál debe aparecer primero. Aquí, ordenamos por gasto total para decir "comenzar con el menor gasto en la parte superior y terminar con el mayor gasto en la parte inferior".
Con ese resultado (esperamos recuperar la copia ordenada de nuestro spendByUser
matriz), llamamos al .pop()
método para decir "quitar el último elemento de la matriz y devolverlo". Aquí, ese último artículo (el cliente con el mayor spend
) se almacena en el mostValuableCustomer
variable. Si desconectamos esta variable, esto es lo que deberíamos obtener cuando ejecutamos nuestro código:
{ mostValuableCustomer: { isLegacyCustomer: false, name: 'Vicente Henry', spend: 15755.03 } }
Terminando
En este tutorial, aprendimos a usar el Array.map
método para recorrer una matriz existente y modificar su contenido. También aprendimos a formatear datos como parte de ese .map()
así como cómo usar los datos resultantes en nuestro código.