Imágenes de: Julie Dorion-Bélanger. Gracias Julie!

Simplifique su JavaScript: use .map (), .reduce () y .filter ()

Si está comenzando en JavaScript, tal vez no haya oído hablar de .map (), .reduce () y .filter (). Para mí, me tomó un tiempo, ya que tuve que soportar Internet Explorer 8 hasta hace un par de años. Pero si no necesita ser compatible con este navegador muy antiguo, debe familiarizarse con esos métodos.

Tenga en cuenta que este artículo probablemente se aplica a cualquier otro lenguaje de programación que pueda estar utilizando, ya que estos son conceptos que existen en muchos otros lenguajes.

.mapa()

Permítanme explicar cómo funciona con un ejemplo simple. Supongamos que ha recibido una matriz que contiene varios objetos, cada uno de los cuales representa a una persona. Sin embargo, lo que realmente necesita al final es una matriz que contenga solo la identificación de cada persona.

// Que tienes
oficiales var = [
  {id: 20, nombre: 'Capitán Piett'},
  {id: 24, nombre: 'General Veers'},
  {id: 56, nombre: 'Almirante Ozzel'},
  {id: 88, nombre: 'Comandante Jerjerrod'}
];
// Que necesitas
[20, 24, 56, 88]

Hay múltiples formas de lograr esto. Es posible que desee hacerlo creando una matriz vacía y luego usando .forEach (), .for (... of) o un simple .for () para cumplir su objetivo.

¡Comparemos!

Usando .forEach ():

var officerIds = [];
officer.forEach (function (officer) {
  officerIds.push (officer.id);
});

¿Te das cuenta de cómo tienes que crear una matriz vacía de antemano? Veamos cómo se ve al usar .map ():

var officerIds = officer.map (function (officer) {
  return officer.id
});

Incluso podemos ser más concisos con las funciones de flecha (requiere compatibilidad con ES6, Babel o TypeScript)

const officerIds = officer.map (officer => officer.id);

Entonces, ¿cómo funciona .map ()? Básicamente se toma 2 argumentos, una devolución de llamada y un contexto opcional (se considerará así en la devolución de llamada) que no utilicé en el ejemplo anterior. La devolución de llamada se ejecuta para cada valor en la matriz y devuelve cada nuevo valor en la matriz resultante.

Tenga en cuenta que la matriz resultante siempre tendrá la misma longitud que la matriz original.

.reducir()

Al igual que .map (), .reduce () también ejecuta una devolución de llamada para cada elemento de una matriz. Lo diferente aquí es que reduce pasa el resultado de esta devolución de llamada (el acumulador) de un elemento de matriz a otro.

El acumulador puede ser prácticamente cualquier cosa (entero, cadena, objeto, etc.) y debe instanciarse o pasarse al llamar a .reduce ().

¡Hora de un ejemplo! Digamos que tiene una variedad con estos pilotos y sus respectivos años de experiencia:

pilotos var = [
  {
    id: 10
    nombre: "Poe Dameron",
    años: 14,
  },
  {
    id: 2
    nombre: "Temmin 'Snap' Wexley",
    años: 30,
  },
  {
    id: 41
    nombre: "Tallissan Lintra",
    años: 16,
  },
  {
    id: 99
    nombre: "Ello Asty",
    años: 22,
  }
];

Necesitamos conocer el total de años de experiencia de todos ellos. Con .reduce (), es bastante sencillo:

var totalYears = pilots.reduce (función (acumulador, piloto) {
  acumulador de retorno + piloto.años;
}, 0);

Tenga en cuenta que he establecido el valor inicial en 0. También podría haber usado una variable existente si fuera necesario. Después de ejecutar la devolución de llamada para cada elemento de la matriz, reduce devolverá el valor final de nuestro acumulador (en nuestro caso: 82).

Veamos cómo se puede acortar esto con las funciones de flecha de ES6:

const totalYears = pilots.reduce ((acc, pilot) => acc + pilot.years, 0);

Ahora digamos que quiero encontrar qué piloto es el más experimentado. Para eso, puedo usar reducir también:

var mostExpPilot = pilots.reduce (función (más antigua, piloto) {
  return (mayores.años || 0)> piloto.años? mayor: piloto;
}, {});

Llamé a mi acumulador más antiguo. Mi devolución de llamada compara el acumulador con cada piloto. Si un piloto tiene más años de experiencia que el más antiguo, entonces ese piloto se convierte en el nuevo más viejo, así que ese es el que regreso.

Como puede ver, usar .reduce () es una manera fácil de generar un único valor u objeto a partir de una matriz.

.filtrar()

¿Qué sucede si tiene una matriz, pero solo quiere algunos de sus elementos? ¡Ahí es donde entra en juego .filter ()!

Aquí están nuestros datos:

pilotos var = [
  {
    id: 2
    nombre: "Wedge Antilles",
    facción: "Rebeldes",
  },
  {
    id: 8
    nombre: "Ciena Ree",
    facción: "Imperio",
  },
  {
    id: 40
    nombre: "Iden Versio",
    facción: "Imperio",
  },
  {
    id: 66
    nombre: "Thane Kyrell",
    facción: "Rebeldes",
  }
];

Digamos que queremos dos conjuntos ahora: uno para los pilotos rebeldes, el otro para los imperiales. ¡Con .filter () no podría ser más fácil!

var rebeldes = pilots.filter (función (piloto) {
  return pilot.faction === "Rebeldes";
});
var empire = pilots.filter (función (piloto) {
  return pilot.faction === "Imperio";
});

¡Eso es! Y es aún más corto con las funciones de flecha:

const rebeldes = pilots.filter (piloto => pilot.faction === "Rebeldes");
const empire = pilots.filter (pilot => pilot.faction === "Empire");

Básicamente, si la función de devolución de llamada devuelve verdadero, el elemento actual estará en la matriz resultante. Si devuelve falso, no lo será.

Combinando .map (), .reduce () y .filter ()

Como los tres se invocan en matrices y dado que .map () y .filter () devuelven matrices, podemos encadenar fácilmente nuestras llamadas.

Veamos otro ejemplo. Aquí están nuestros datos:

var personal = [
  {
    id: 5
    nombre: "Luke Skywalker",
    puntaje de pilotaje: 98,
    Puntuación: 56,
    isForceUser: verdadero,
  },
  {
    id: 82
    nombre: "Sabine Wren",
    puntaje de pilotaje: 73,
    ShootingScore: 99,
    isForceUser: falso,
  },
  {
    id: 22
    nombre: "Zeb Orellios",
    puntaje de pilotaje: 20,
    Puntuación: 59,
    isForceUser: falso,
  },
  {
    id: 15
    nombre: "Ezra Bridger",
    puntaje de pilotaje: 43,
    Puntuación: 67,
    isForceUser: verdadero,
  },
  {
    id: 11
    nombre: "Caleb Dume",
    puntaje de pilotaje: 71,
    Puntuación: 85,
    isForceUser: verdadero,
  },
];

Nuestro objetivo: obtener la puntuación total de los usuarios de la fuerza solamente. ¡Hagámoslo paso a paso!

Primero, necesitamos filtrar al personal que no puede usar la fuerza:

var jediPersonnel = staff.filter (función (persona) {
  return person.isForceUser;
});
// Resultado: [{...}, {...}, {...}] (Luke, Ezra y Caleb)

Con eso nos quedan 3 elementos en nuestra matriz resultante. Ahora necesitamos crear una matriz que contenga la puntuación total de cada Jedi.

var jediScores = jediPersonnel.map (function (jedi) {
  devuelve jedi.pilotingScore + jedi.shootingScore;
});
// Resultado: [154, 110, 156]

Y usemos reducir para obtener el total:

var totalJediScore = jediScores.reduce (function (acc, score) {
  retorno acc + puntaje;
}, 0);
// Resultado: 420

Y ahora esta es la parte divertida ... podemos encadenar todo esto para obtener lo que queremos en una sola línea:

var totalJediScore = personal
  .filter (función (persona) {
    return person.isForceUser;
  })
  .map (función (jedi) {
    devuelve jedi.pilotingScore + jedi.shootingScore;
  })
  .reduce (función (acc, score) {
    retorno acc + puntaje;
  }, 0);

Y mira qué bonito es con las funciones de flecha:

const totalJediScore = personal
  .filter (persona => person.isForceUser)
  .map (jedi => jedi.pilotingScore + jedi.shootingScore)
  .reduce ((acc, score) => acc + score, 0);

¡Auge!

Nota: en mi ejemplo anterior, .map () y .filter () ni siquiera eran necesarios. Podríamos lograr fácilmente el mismo resultado con solo .reduce (). Los dejé allí por el bien de este ejemplo. ¿Puedes adivinar cómo podríamos mantener .reduce () y obtener el mismo resultado con una sola línea de código? Vea la solución en CodePen

¿Por qué no usar .forEach ()?

Solía ​​usar bucles en todas partes en lugar de .map (), .reduce () y .filter (). Pero hace un par de años comencé a trabajar mucho más con datos que provenían de una API. Ahí es donde comencé a ver las ventajas de dejar atrás .forEach.

Formateo

Digamos que necesita mostrar una lista de personas, con su nombre y cargo.

datos var = [
  {
    nombre: "Jan Dodonna",
    título: "General",
  },
  {
    nombre: "Gial Ackbar",
    título: "Almirante",
  },
]

La API le proporciona los datos anteriores, pero solo necesita el título y el apellido de cada persona ... Necesita formatear los datos. Sin embargo, su aplicación también debe tener una vista única para cada persona, por lo que debe escribir una función de formato de datos que funcione tanto en una vista de lista como en una vista única.

Eso significa que no puede tener el bucle .forEach dentro de su función de formateo, o de lo contrario tendría que envolver su elemento individual en una matriz antes de pasarlo a la función solo para que funcione, así:

resultado var = formatElement ([elemento]) [0];
// Sí ... eso no está bien en absoluto

Entonces su bucle tiene que ajustar la llamada de la función, así:

data.forEach (function (element) {
  var formatted = formatElement (elemento);
  // Pero entonces que ...
});

Pero .forEach () no devuelve nada. No puede Eso significa que debe empujar los resultados dentro de una matriz predeterminada.

resultados var = [];
data.forEach (function (element) {
  var formatted = formatElement (elemento);
  resultados.push (formateado);
});

Como resultado, tiene 2 funciones: su función formatElement () y su función que empuja los resultados en su matriz.

¿Por qué tener 2 funciones cuando puedes tener solo una?

resultados var = data.map (formatElement);

La prueba es más fácil

Si escribe pruebas unitarias para su código, le resultará más sencillo probar las funciones que llama con .map (), .reduce () o .filter ().

Todo lo que tiene que hacer es proporcionar datos entrantes para la función y esperar que salga un resultado. Básicamente "¿qué sale si esto se aprueba?". Menos manipulación, menos beforeEach () sy afterEach () s. Es una prueba simple y directa.

¡Intentalo!

Intente reemplazar algunos de sus bucles for con .map (), .reduce (), .filter () donde parece encajar. Le garantizo que su código será mucho menos torpe y mucho más fácil de leer.

Si le gustó ese artículo y quiere aprender más métodos de matriz, consulte mi artículo sobre cómo usar .some () y .find () en JavaScript.

¡Sigue codificando!