Para los desarrolladores de JavaScript, Lodash no necesita presentación. Sin embargo, la biblioteca es enorme y, a menudo, se siente abrumadora. ¡Ya no!

Lodash, Lodash, Lodash. . . ¡Por dónde empiezo! 🤔

Hubo un tiempo en que el ecosistema de JavaScript era incipiente; podría compararse con el salvaje oeste o una jungla si se quiere, donde estaban sucediendo muchas cosas, pero había muy pocas respuestas para las frustraciones y la productividad diarias de los desarrolladores.

Entonces Lodash entró en escena, y se sintió como una inundación que lo sumergió todo. Desde las necesidades cotidianas simples como la clasificación hasta las transformaciones complejas de la estructura de datos, Lodash vino cargado (¡sobrecargado, incluso!) Con una funcionalidad que convirtió la vida de los desarrolladores de JS en una pura felicidad.

¡Hola, Lodash!

¿Y dónde está Lodash hoy? Bueno, todavía tiene todas las ventajas que ofrecía inicialmente, y luego algunas, pero parece haber perdido la cabeza en la comunidad de JavaScript. ¿Por qué? Puedo pensar en algunas razones:

  • Algunas funciones de la biblioteca Lodash eran (y siguen siendo) lentas cuando se aplican a listas grandes. Si bien esto nunca habría afectado al 95% de los proyectos, los desarrolladores influyentes del 5% restante le dieron a Lodash una mala prensa y el efecto se extendió a las bases.
  • Hay una tendencia en el ecosistema JS (incluso podría decir lo mismo de la gente de Golang) donde la arrogancia es más común de lo necesario. Por lo tanto, confiar en algo como Lodash se considera una estupidez y se rechaza en foros como StackOverflow cuando la gente sugiere tales soluciones ("¿Qué ?! ¿Usar una biblioteca completa para algo como esto? Puedo combinar filter() con reduce() para lograr lo mismo en una función simple! ”).
  • Lodash es viejo. Al menos según los estándares de JS. Salió en 2012, así que hasta el momento de escribirlo, han pasado casi diez años. los API ha sido estable, y no se pueden agregar muchas cosas interesantes cada año (simplemente porque no es necesario), lo que genera aburrimiento para el desarrollador de JS sobreexcitado promedio.

En mi opinión, no usar Lodash es una pérdida significativa para nuestro JavaScript bases de código. Ha probado soluciones elegantes y libres de errores para los problemas cotidianos con los que nos encontramos en el trabajo, y su uso solo hará que nuestro código sea más legible y fácil de mantener.

Dicho esto, profundicemos en algunas de las funciones Lodash comunes (¡o no!) Y veamos cuán increíblemente útil y hermosa es esta biblioteca.

Clone . . . deeply!

Dado que los objetos se pasan por referencia en JavaScript, crea un dolor de cabeza para los desarrolladores cuando quieren clonar algo con la esperanza de que el nuevo conjunto de datos sea diferente.

let people = [
  {
    name: 'Arnold',
    specialization: 'C++',
  },
  {
    name: 'Phil',
    specialization: 'Python',
  },
  {
    name: 'Percy',
    specialization: 'JS',
  },
];

// Find people writing in C++
let folksDoingCpp = people.filter((person) => person.specialization == 'C++');

// Convert them to JS!
for (person of folksDoingCpp) {
  person.specialization = 'JS';
}

console.log(folksDoingCpp);
// [ { name: 'Arnold', specialization: 'JS' } ]

console.log(people);
/*
[
  { name: 'Arnold', specialization: 'JS' },
  { name: 'Phil', specialization: 'Python' },
  { name: 'Percy', specialization: 'JS' }
]
*/

Nótese cómo en nuestra pura inocencia y a pesar de nuestras buenas intenciones, el original people matriz mutó en el proceso (la especialización de Arnold cambió de C++ a JS) - ¡un gran golpe para la integridad del sistema de software subyacente! De hecho, necesitamos una forma de hacer una copia verdadera (profunda) de la matriz original.

Hola Dave, ¡te presento a Dave!

Quizás pueda argumentar que esta es una forma “tonta” de codificar en JS; sin embargo, la realidad es un poco complicada. Sí, tenemos disponible el encantador operador de desestructuración, pero cualquiera que haya intentado desestructurar objetos y matrices complejas conoce el dolor. Luego, está la idea de usar serialización y deserialización (tal vez JSON) para lograr una copia profunda, pero solo hace que su código sea más complicado para el lector.

Por el contrario, mire lo increíblemente elegante y concisa que es la solución cuando se acostumbra a Lodash:

const _ = require('lodash');

let people = [
  {
    name: 'Arnold',
    specialization: 'C++',
  },
  {
    name: 'Phil',
    specialization: 'Python',
  },
  {
    name: 'Percy',
    specialization: 'JS',
  },
];

let peopleCopy = _.cloneDeep(people);

// Find people writing in C++
let folksDoingCpp = peopleCopy.filter(
  (person) => person.specialization == 'C++'
);

// Convert them to JS!
for (person of folksDoingCpp) {
  person.specialization = 'JS';
}

console.log(folksDoingCpp);
// [ { name: 'Arnold', specialization: 'JS' } ]

console.log(people);
/*
[
  { name: 'Arnold', specialization: 'C++' },
  { name: 'Phil', specialization: 'Python' },
  { name: 'Percy', specialization: 'JS' }
]
*/

Note como el people matriz está intacta después de la clonación profunda (Arnold todavía se especializa en C++ en este caso). Pero lo que es más importante, el código es sencillo de entender.

Remove duplicates from an array

Eliminar duplicados de una matriz suena como un excelente problema de entrevista / pizarra (recuerde, en caso de duda, ¡arroje un mapa de hash al problema!). Y, por supuesto, siempre puede escribir una función personalizada para hacer eso, pero ¿qué pasa si se encuentra con varios escenarios diferentes en los que hacer que sus matrices sean únicas? Podrías escribir varios otras funciones para eso (y arriesgarse a encontrar errores sutiles), ¡o simplemente podría usar Lodash!

Nuestro primer ejemplo de matrices únicas es bastante trivial, pero aún representa la velocidad y confiabilidad que Lodash aporta. ¡Imagínese haciendo esto escribiendo toda la lógica personalizada usted mismo!

const _ = require('lodash');

const userIds = [12, 13, 14, 12, 5, 34, 11, 12];
const uniqueUserIds = _.uniq(userIds);
console.log(uniqueUserIds);
// [ 12, 13, 14, 5, 34, 11 ]

Tenga en cuenta que la matriz final no está ordenada, lo que, por supuesto, no tiene importancia aquí. Pero ahora, imaginemos un escenario más complicado: tenemos una serie de usuarios que extrajimos de algún lugar, pero queremos asegurarnos de que solo contenga usuarios únicos. ¡Fácil con Lodash!

const _ = require('lodash');

const users = [
  { id: 10, name: 'Phil', age: 32 },
  { id: 8, name: 'Jason', age: 44 },
  { id: 11, name: 'Rye', age: 28 },
  { id: 10, name: 'Phil', age: 32 },
];

const uniqueUsers = _.uniqBy(users, 'id');
console.log(uniqueUsers);
/*
[
  { id: 10, name: 'Phil', age: 32 },
  { id: 8, name: 'Jason', age: 44 },
  { id: 11, name: 'Rye', age: 28 }
]
*/

En este ejemplo, usamos el uniqBy() método para decirle a Lodash que queremos que los objetos sean únicos en el id propiedad. En una línea, expresamos lo que podría haber tomado de 10 a 20 líneas e introducido más posibilidades de errores.

Hay muchas más cosas disponibles para hacer que las cosas sean únicas en Lodash, y te animo a que eches un vistazo a documentos.

Difference of two arrays

Unión, diferencia, etc., pueden parecer términos que es mejor dejar atrás en las aburridas conferencias de la escuela secundaria sobre teoría de conjuntos, pero aparecen con mayor frecuencia en la práctica diaria. Es común tener una lista y querer fusionar otra lista con ella o querer encontrar qué elementos son exclusivos de ella en comparación con otra lista; para estos escenarios, la función de diferencia es perfecta.

Hola, A. ¡Adiós, B!

Comencemos el viaje de la diferencia tomando un escenario simple: ha recibido una lista de todos los identificadores de usuario en el sistema, así como una lista de aquellos cuyas cuentas están activas. ¿Cómo encuentras los identificadores inactivos? Simple, ¿verdad?

const _ = require('lodash');

const allUserIds = [1, 3, 4, 2, 10, 22, 11, 8];
const activeUserIds = [1, 4, 22, 11, 8];

const inactiveUserIds = _.difference(allUserIds, activeUserIds);
console.log(inactiveUserIds);
// [ 3, 2, 10 ]

¿Y si, como sucede en un entorno más realista, tienes que trabajar con una matriz de objetos en lugar de simples primitivas? Bueno, Lodash tiene un buen differenceBy() método para esto!

const allUsers = [
  { id: 1, name: 'Phil' },
  { id: 2, name: 'John' },
  { id: 3, name: 'Rogg' },
];
const activeUsers = [
  { id: 1, name: 'Phil' },
  { id: 2, name: 'John' },
];
const inactiveUsers = _.differenceBy(allUsers, activeUsers, 'id');
console.log(inactiveUsers);
// [ { id: 3, name: 'Rogg' } ]

¡¿Limpio, verdad ?!

Como diferencia, hay otros métodos en Lodash para operaciones de conjuntos comunes: unión, intersección, etc.

Flattening arrays

La necesidad de aplanar matrices surge con bastante frecuencia. Un caso de uso es que ha recibido una respuesta de API y necesita aplicar algunos map() y filter()combo en una lista compleja de objetos / matrices anidadas para extraer, digamos, identificadores de usuario, y ahora te quedan matrices de matrices. Aquí hay un fragmento de código que describe esta situación:

const orderData = {
  internal: [
    { userId: 1, date: '2021-09-09', amount: 230.0, type: 'prepaid' },
    { userId: 2, date: '2021-07-07', amount: 130.0, type: 'prepaid' },
  ],
  external: [
    { userId: 3, date: '2021-08-08', amount: 30.0, type: 'postpaid' },
    { userId: 4, date: '2021-06-06', amount: 330.0, type: 'postpaid' },
  ],
};

// find user ids that placed postpaid orders (internal or external)
const postpaidUserIds = [];

for (const [orderType, orders] of Object.entries(orderData)) {
  postpaidUserIds.push(orders.filter((order) => order.type === 'postpaid'));
}
console.log(postpaidUserIds);

Puedes adivinar que postPaidUserIds ahora parece? Pista: ¡es repugnante!

[
  [],
  [
    { userId: 3, date: '2021-08-08', amount: 30, type: 'postpaid' },
    { userId: 4, date: '2021-06-06', amount: 330, type: 'postpaid' }
  ]
]

Ahora, si eres una persona sensata, no querrás escribir una lógica personalizada para extraer los objetos de orden y colocarlos bien en una fila dentro de una matriz. Solo usa el flatten() método y disfruta de las uvas:

const flatUserIds = _.flatten(postpaidUserIds);
console.log(flatUserIds);
/*
[
  { userId: 3, date: '2021-08-08', amount: 30, type: 'postpaid' },
  { userId: 4, date: '2021-06-06', amount: 330, type: 'postpaid' }
]
*/

Tenga en cuenta que flatten() solo llega a un nivel de profundidad. Es decir, si sus objetos están atascados a dos, tres o más niveles de profundidad, flatten() te decepcionarán. En esos casos, Lodash tiene la flattenDeep() método, pero tenga en cuenta que la aplicación de este método en estructuras muy grandes puede ralentizar las cosas (ya que detrás de escena, hay una operación recursiva en el trabajo).

Is the object/array empty?

Gracias a cómo funcionan los valores y tipos "falsos" en JavaScript, a veces algo tan simple como verificar el vacío resulta en pavor existencial.

¿Cómo se comprueba si una matriz está vacía? Puede comprobar si es length is 0 o no. Ahora, ¿cómo se comprueba si un objeto está vacío? Bueno… ¡espera un minuto! Aquí es donde ese sentimiento de inquietud conjuntos en, y esos ejemplos de JavaScript que contienen cosas como [] == false y {} == false empezar a dar vueltas en nuestras cabezas. Cuando está bajo presión para ofrecer una función, las minas terrestres como estas son lo último que necesita: harán que su código sea difícil de entender e introducirán incertidumbre en su conjunto de pruebas.

Working with missing data

En el mundo real, los datos nos escuchan; No importa lo mucho que lo deseemos, rara vez es racionalizado y cuerdo. Un ejemplo típico es la falta de objetos / matrices nulos en una gran estructura de datos recibida como respuesta de la API.

Supongamos que recibimos el siguiente objeto como respuesta de la API:

const apiResponse = {
  id: 33467,
  paymentRefernce: 'AEE3356T68',
  // `order` object missing
  processedAt: `2021-10-10 00:00:00`,
};

Como se muestra, generalmente obtenemos una order Objeto en la respuesta de la API, pero no siempre es así. Entonces, ¿qué pasa si tenemos algún código que se basa en este objeto? Una forma sería codificar a la defensiva, pero dependiendo de qué tan anidado order El objeto es que pronto estaremos escribiendo un código muy feo si deseamos evitar errores en tiempo de ejecución:

if (
  apiResponse.order &&
  apiResponse.order.payee &&
  apiResponse.order.payee.address
) {
  console.log(
    'The order was sent to the zip code: ' +
      apiResponse.order.payee.address.zipCode
  );
}

🤢🤢 Sí, muy feo de escribir, muy feo de leer, muy feo de mantener, etc. Afortunadamente, Lodash tiene una forma sencilla de lidiar con tales situaciones.

const zipCode = _.get(apiResponse, 'order.payee.address.zipCode');
console.log('The order was sent to the zip code: ' + zipCode);
// The order was sent to the zip code: undefined

También existe la fantástica opción de proporcionar un valor predeterminado en lugar de obtener undefined por faltar cosas:

const zipCode2 = _.get(apiResponse, 'order.payee.address.zipCode', 'NA');
console.log('The order was sent to the zip code: ' + zipCode2);
// The order was sent to the zip code: NA

No se tu pero get() es una de esas cosas que me hacen llorar de felicidad. No es nada llamativo. No hay sintaxis consultada ni opciones para memorizar, ¡pero mira la cantidad de sufrimiento colectivo que puede aliviar! 😇

Debouncing

En caso de que no esté familiarizado, la eliminación de rebotes es un tema común en el desarrollo de frontend. La idea es que a veces es beneficioso lanzar una acción no inmediatamente sino después de un tiempo (generalmente, unos pocos milisegundos). ¿Qué significa eso? He aquí un ejemplo.

Imagine un sitio web de comercio electrónico con una barra de búsqueda (bueno, ¡cualquier sitio web / aplicación web en estos días!). Para una mejor experiencia de usuario, no queremos que el usuario tenga que presionar enter (o peor aún, presionar el botón "buscar") para mostrar sugerencias / vistas previas basadas en su término de búsqueda. Pero la respuesta obvia está un poco cargada: si agregamos un detector de eventos a onChange() para la barra de búsqueda y lanzar una llamada a la API por cada pulsación de tecla, habríamos creado una pesadilla para nuestro backend; habría demasiadas llamadas innecesarias (por ejemplo, si se busca “cepillo de alfombra blanca”, habrá un total de 18 solicitudes) y casi todas serán irrelevantes porque la entrada del usuario no ha terminado.

La respuesta está en la eliminación de rebotes, y la idea es la siguiente: no envíe una llamada a la API tan pronto como cambie el texto. Espere un tiempo (digamos, 200 milisegundos) y si en ese momento hay otra pulsación de tecla, cancele el conteo de tiempo anterior y vuelva a comenzar a esperar. Como resultado, es solo cuando el usuario hace una pausa (ya sea porque está pensando o porque ha terminado y espera alguna respuesta) que enviamos una solicitud de API al backend.

La estrategia general que describí es complicada y no me sumergiré en la sincronización de la gestión y cancelación del temporizador; sin embargo, el proceso de eliminación de rebotes real es muy simple si está utilizando Lodash.

const _ = require('lodash');
const axios = require('axios');

// This is a real dogs' API, by the way!
const fetchDogBreeds = () =>
  axios
    .get('https://dog.ceo/api/breeds/list/all')
    .then((res) => console.log(res.data));

const debouncedFetchDogBreeds = _.debounce(fetchDogBreeds, 1000); // after one second
debouncedFetchDogBreeds(); // shows data after some time

Si estas pensando setTimeout() Yo hubiera hecho el mismo trabajo, bueno, ¡hay más! El antirrebote de Lodash viene con muchas características poderosas; por ejemplo, es posible que desee asegurarse de que el rebote no sea indefinido. Es decir, incluso si hay una pulsación de tecla cada vez que la función está a punto de activarse (cancelando así el proceso general), es posible que desee asegurarse de que la llamada a la API se realice de todos modos después de, digamos, dos segundos. Por esto, Lodash debounce() tiene la maxWait opción:

const debouncedFetchDogBreeds = _.debounce(fetchDogBreeds, 150, { maxWait: 2000 }); // debounce for 250ms, but send the API request after 2 seconds anyway

Echa un vistazo al oficial documentos para una inmersión más profunda. ¡Están llenos de cosas súper importantes!

Remove values from an array

No sé ustedes, pero odio escribir código para eliminar elementos de una matriz. Primero, tengo que obtener el índice del elemento, verificar si el índice es realmente válido y, de ser así, llamar al splice() método, y así sucesivamente. Nunca puedo recordar la sintaxis y, por lo tanto, necesito buscar las cosas todo el tiempo, y al final, me quedo con la molesta sensación de que he dejado que un error estúpido se cuele.

const greetings = ['hello', 'hi', 'hey', 'wave', 'hi'];
_.pull(greetings, 'wave', 'hi');
console.log(greetings);
// [ 'hello', 'hey' ]

Tenga en cuenta dos cosas:

  1. La matriz original se cambió en el proceso.
  2. El proceso de la pull() El método elimina todas las instancias, incluso si hay duplicados.

Hay otro método relacionado llamado pullAll() que acepta una matriz como segundo parámetro, lo que facilita la eliminación de varios elementos a la vez. Concedido que podríamos usar pull() con un operador de propagación, pero recuerde que Lodash llegó en un momento en que el operador de propagación ni siquiera era una propuesta en el idioma.

const greetings2 = ['hello', 'hi', 'hey', 'wave', 'hi'];
_.pullAll(greetings2, ['wave', 'hi']);
console.log(greetings2);
// [ 'hello', 'hey' ]

Last index of an element

Nativo de JavsScript indexOf() ¡El método es genial, excepto cuando está interesado en escanear la matriz desde la dirección opuesta! Y una vez más, sí, podría simplemente escribir un bucle decreciente y encontrar el elemento, pero ¿por qué no usar una técnica mucho más elegante?

Aquí hay una solución rápida de Lodash usando el lastIndexOf() método:

const integers = [2, 4, 1, 6, -1, 10, 3, -1, 7];
const index = _.lastIndexOf(integers, -1);
console.log(index); // 7

Desafortunadamente, no hay una variante de este método en la que podamos buscar objetos complejos o incluso pasar una función de búsqueda personalizada.

Zip. Unzip!

A menos que hayas trabajado en Python, zip / unzip es una utilidad que quizás nunca notará o imaginará en toda su carrera como desarrollador de JavaScript. Y quizás por una buena razón: rara vez existe el tipo de necesidad desesperada de comprimir / descomprimir como existe para filter(), etc. Sin embargo, es una de las mejores utilidades menos conocidas y puede ayudarlo a crear código sucinto en algunas situaciones.

Al contrario de lo que parece, zip / unzip no tiene nada que ver con la compresión. En cambio, es una operación de agrupación donde las matrices de la misma longitud se pueden convertir en una única matriz de matrices con elementos en la misma posición empaquetados juntos (zip()) y de regreso (unzip()). Sí, lo sé, se está volviendo confuso tratar de arreglárselas con palabras, así que veamos un código:

const animals = ['duck', 'sheep'];
const sizes = ['small', 'large'];
const weight = ['less', 'more'];

const groupedAnimals = _.zip(animals, sizes, weight);
console.log(groupedAnimals);
// [ [ 'duck', 'small', 'less' ], [ 'sheep', 'large', 'more' ] ]

Los tres arreglos originales se convirtieron en uno solo con dos arreglos solamente. Y cada una de estas nuevas matrices representa un solo animal con todo en un solo lugar. Entonces, el índice 0 nos dice qué tipo de animal es, el índice 1 nos dice su tamaño y el índice 2 nos dice su peso. Como resultado, ahora es más fácil trabajar con los datos. Una vez que haya aplicado las operaciones que necesite en los datos, puede dividirlos nuevamente usando unzip() y envíelo de vuelta a la fuente original:

const animalData = _.unzip(groupedAnimals);
console.log(animalData);
// [ [ 'duck', 'sheep' ], [ 'small', 'large' ], [ 'less', 'more' ] ]

La utilidad zip / unzip no es algo que cambiará tu vida de la noche a la mañana, ¡pero cambiará tu vida algún día!

Conclusión 👨🏫

(Pongo todo el código fuente usado en este artículo aquí para que lo pruebes directamente desde el navegador!)

El Lodash documentos están repletas de ejemplos y funciones que te dejarán boquiabierto. En una época en la que el masoquismo parece estar aumentando en el ecosistema de JS, Lodash es como un soplo de aire fresco, ¡y le recomiendo que utilice esta biblioteca en sus proyectos!