A veces necesitas iterar sobre un array y crear uno nuevo basado en el array original. Tal vez, dada una lista de personas, necesites solo una lista de los nombres completos.
Consideremos este array de los primeros seis presidentes de los Estados Unidos de América.
const presidents = [
{
firstName: "George",
lastName: "Washington",
party: "Independent",
},
{
firstName: "John",
lastName: "Adams",
party: "Federalist",
},
{
firstName: "Thomas",
lastName: "Jefferson",
party: "Democratic-Republican",
},
{
firstName: "James",
lastName: "Madison",
party: "Democratic-Republican",
},
{
firstName: "James",
lastName: "Monroe",
party: "Democratic-Republican",
},
];
Resolver esto con forEach
se vería así:
const names = [];
presidents.forEach((president) => {
names.push(`${president.firstName} ${president.lastName}`);
});
Usando map
, podemos lograr lo mismo:
const names = presidents.map(
(president) => `${president.firstName} ${president.lastName}`,
);
Otro caso de uso es eliminar entradas que no cumplan con ciertos criterios. De nuestra lista de presidentes, digamos que solo quieres ver aquellos del partido "Democratic-Republican".
Resolver esto con forEach
se vería así:
const drPresidents = [];
presidents.forEach((president) => {
if (president.party === "Democratic-Republican") {
drPresidents.push(president);
}
});
Usando filter
, podemos lograr lo mismo:
const drPresidents = presidents.filter(
(president) => president.party === "Democratic-Republican",
);
Otro caso de uso es crear un nuevo array solo a partir de algunos de los elementos originales. Continuemos considerando la lista de presidentes. Digamos que queremos una lista de los nombres completos de los presidentes del partido "Democratic-Republican".
Resolver esto con forEach
se vería así:
const drPresidents = [];
presidents.forEach((president) => {
if (president.party === "Democratic-Republican") {
drPresidents.push(`${president.firstName} ${president.lastName}`);
}
});
Podemos combinar los dos métodos que acabamos de revisar, map
y filter
para lograr lo mismo:
const drPresidents = presidents
.filter(president => president.party === "Democratic-Republican")
.map(president => `${president.firstName} ${president.lastName}`);
Esto iterará sobre la lista dos veces, lo cual puede no ser deseable si trabajas con grandes conjuntos de datos. Si quieres recorrer el array solo una vez, puedes usar reduce
para lograr lo mismo:
const drPresidents = presidents.reduce((acc, president) => {
if (president.party === "Democratic-Republican") {
acc.push(`${president.firstName} ${president.lastName}`);
}
return acc;
}, []);
Alternativamente, también puedes usar flatMap
para obtener el mismo resultado:
const drPresidents = presidents.flatMap((president) =>
president.party === "Democratic-Republican"
? `${president.firstName} ${president.lastName}`
: [],
);
Esto funciona porque flatMap
"aplanará" el array vacío fuera del resultado final.
Otro caso de uso común es agrupar un array por alguna propiedad. Si consideramos nuestro array de presidentes, tal vez queramos agruparlos por su partido. Queremos un objeto donde las claves sean los nombres de los partidos y los valores sean los presidentes de ese partido. Un resultado final de:
const presidentsByParty = {
Independent: [
{
firstName: "George",
lastName: "Washington",
party: "Independent",
},
],
Federalist: [
{
firstName: "John",
lastName: "Adams",
party: "Federalist",
},
],
"Democratic-Republican": [
{
firstName: "Thomas",
lastName: "Jefferson",
party: "Democratic-Republican",
},
{
firstName: "James",
lastName: "Madison",
party: "Democratic-Republican",
},
{
firstName: "James",
lastName: "Monroe",
party: "Democratic-Republican",
},
],
};
Podemos lograr esto usando el método forEach
de la siguiente manera:
const presidentsByParty = {};
presidents.forEach((president) => {
if (!presidentsByParty[president.party]) {
presidentsByParty[president.party] = [];
}
presidentsByParty[president.party].push(president);
});
Evitando forEach
puedes usar reduce
para lograr lo mismo:
const presidentsByParty = presidents.reduce((acc, president) => {
if (!acc[president.party]) {
acc[president.party] = [];
}
acc[president.party].push(president);
return acc;
}, {});
Alternativamente, puedes usar el método Object.groupBy
para hacer esto:
const presidentsByParty = Object.groupBy(
presidents,
(president) => president.party,
);
Dado un array con tipos de datos simples (cadenas o números) podrías querer ver si un cierto valor está en el array.
const scores = [99, 92, 40, 47, 83, 100, 82];
let hasPerfectScore = false;
scores.forEach((score) => {
if (score === 100) {
hasPerfectScore = true;
}
});
Esto iterará sobre cada elemento en el array, incluso después de encontrar una coincidencia.
Podemos usar el método .includes
para hacer lo mismo y se detendrá después de encontrar una coincidencia.
const scores = [99, 92, 40, 47, 83, 100, 82];
const hasPerfectScore = scores.includes(100);
Cuando usas includes
, solo puedes usarlo con tipos de datos simples, siendo string
y number
los más comunes. Si quieres comparar objetos, tendrás que usar el método some
. Primero, veamos cómo harías esto con el método forEach
.
const students = [
{ name: "Adam", score: 99 },
{ name: "Bryan", score: 92 },
{ name: "Calvin", score: 40 },
{ name: "Douglas", score: 47 },
{ name: "Edward", score: 83 },
{ name: "Fred", score: 100 },
{ name: "Georg", score: 82 },
];
let hasPerfectScore = false;
students.forEach((student) => {
if (student.score === 100) {
hasPerfectScore = true;
}
});
Usando el método some
tenemos:
const hasPerfectScore = students.some((student) => student.score === 100);
Otro caso de uso es comprobar que todos los elementos cumplan con ciertos criterios. Por ejemplo, podrías querer saber si cada estudiante aprobó el examen. Usando forEach
, tendrías:
const MINIMUM_PASSING_SCORE = 60;
let didEveryStudentPass = true;
students.forEach((student) => {
if (student.score < MINIMUM_PASSING_SCORE) {
didEveryStudentPass = false;
}
});
Como siempre, forEach
recorrerá todos los elementos en el array, incluso después de encontrar un caso en el que un estudiante no aprobó.
Aquí podemos usar el método every
, que se detendrá tan pronto como uno de los elementos no cumpla con los criterios.
const MINIMUM_PASSING_SCORE = 60;
const didEveryStudentPass = students.every(
(student) => student.score >= MINIMUM_PASS
ING_SCORE,
);
Puedes lograr lo mismo usando cualquier método negando el resultado y negando la condición. Por ejemplo, los dos siguientes son equivalentes.
const MINIMUM_PASSING_SCORE = 60;
const didEveryStudentPassEvery = students.every(
(student) => student.score >= MINIMUM_PASSING_SCORE,
);
const didEveryStudentPassSome = !students.some(
(student) => student.score < MINIMUM_PASSING_SCORE,
);
Usar some
en lo anterior, puede expresarse en forma larga con lo siguiente, que es más intuitivo de entender.
const didSomeStudentFail = students.some(
(student) => student.score < MINIMUM_PASSING_SCORE,
);
const didEveryStudentPass = !didSomeStudentFail;
Sin embargo, usar dobles negativos es menos intuitivo y más difícil de razonar. Usar la variación más simple sería preferible.
Entonces, aunque puedes usar forEach
para lograr todos estos casos de uso, hay funciones más específicas que pueden hacer el trabajo también. Hay dos beneficios a considerar con los métodos alternativos.
some
, every
, find
e includes
será más rápido ya que se detienen tan pronto como encuentran un resultado.
¡Espero que esto haya sido útil y que hayas aprendido algo nuevo que puedas usar en tus proyectos!
Jorge García
Fullstack developer