10 funções Lodash importantes para desenvolvedores de JavaScript

Publicados: 2021-10-05

Para desenvolvedores de JavaScript, o Lodash dispensa apresentações. No entanto, a biblioteca é vasta e muitas vezes parece opressora. Não mais!

Lodash, Lodash, Lodash. . . por onde eu começo!

Houve um tempo em que o ecossistema JavaScript estava nascendo; ele poderia ser comparado ao oeste selvagem ou a uma selva, se você quiser, onde muito estava acontecendo, mas havia muito poucas respostas para as frustrações e produtividade diárias dos desenvolvedores.

Então Lodash entrou em cena e parecia uma inundação que submergiu tudo. Desde as necessidades diárias simples, como classificação, até transformações complexas da estrutura de dados, o Lodash veio carregado (até mesmo sobrecarregado!) Com funcionalidades que transformaram a vida dos desenvolvedores de JS em pura felicidade.

Olá, Lodash!

E onde está Lodash hoje? Bem, ele ainda tem todas as vantagens que oferecia inicialmente, e mais alguns, mas parece ter perdido a participação na comunidade JavaScript. Por quê? Posso pensar em alguns motivos:

  • Algumas funções da biblioteca Lodash eram (e ainda são) lentas quando aplicadas a listas grandes. Embora isso nunca tivesse afetado 95% dos projetos por aí, desenvolvedores influentes dos 5% restantes deram a Lodash uma má impressão e o efeito se espalhou pelas bases.
  • Há uma tendência no ecossistema JS (pode até dizer a mesma coisa sobre o pessoal de Golang) em que a arrogância é mais comum do que o necessário. Então, confiar em algo como Lodash é visto como estúpido e é rejeitado em fóruns como StackOverflow quando as pessoas sugerem tais soluções (“O quê ?! Usar uma biblioteca inteira para algo assim? Posso combinar filter() com reduce() para alcançar a mesma coisa em uma função simples! ”).
  • Lodash é velho. Pelo menos pelos padrões JS. Saiu em 2012, então, enquanto escrevo, já se passaram quase dez anos. A API tem estado estável e não podem ser adicionadas muitas coisas interessantes todos os anos (simplesmente porque não há necessidade), o que gera tédio para o desenvolvedor JS superexcitado.

Na minha opinião, não usar o Lodash é uma perda significativa para nossas bases de código JavaScript. Ele provou soluções elegantes e sem bugs para os problemas diários que encontramos no trabalho, e usá-lo apenas tornará nosso código mais legível e sustentável.

Com isso dito, vamos mergulhar em algumas das funções comuns (ou não!) Do Lodash e ver como essa biblioteca é incrivelmente útil e bonita.

Clone. . . profundamente!

Como os objetos são passados ​​por referência em JavaScript, isso cria uma dor de cabeça para os desenvolvedores quando desejam clonar algo na esperança de que o novo conjunto de dados seja 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' } ] */

Observe como em nossa pura inocência e apesar de nossas boas intenções, o array original people sofreu mutação no processo (a especialização de Arnold mudou de C++ para JS ) - um grande golpe para a integridade do sistema de software subjacente! Na verdade, precisamos de uma maneira de fazer uma cópia verdadeira (profunda) do array original.

Oi Dave, conheça o Dave!

Você pode talvez argumentar que esta é uma maneira “boba” de codificação em JS; no entanto, a realidade é um pouco complicada. Sim, temos o adorável operador de desestruturação disponível, mas qualquer um que já tentou desestruturar objetos e matrizes complexos conhece a dor. Então, há a ideia de usar serialização e desserialização (talvez JSON) para obter cópias profundas, mas isso apenas torna seu código mais confuso para o leitor.

Em contraste, veja como é incrivelmente elegante e concisa a solução quando o Lodash é usado:

 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' } ] */

Observe como a matriz de people permanece intacta após a clonagem profunda (Arnold ainda é especialista em C++ neste caso). Mas o mais importante é que o código é simples de entender.

Remova duplicatas de uma matriz

Remover duplicatas de um array soa como um excelente problema de entrevista / quadro branco (lembre-se, em caso de dúvida, jogue um hashmap no problema!). E, é claro, você sempre pode escrever uma função personalizada para fazer isso, mas e se você encontrar vários cenários diferentes nos quais tornar seus arrays exclusivos? Você pode escrever várias outras funções para isso (e correr o risco de encontrar bugs sutis) ou pode apenas usar o Lodash!

Nosso primeiro exemplo de matrizes exclusivas é bastante trivial, mas ainda representa a velocidade e a confiabilidade que Lodash traz para a mesa. Imagine fazer isso escrevendo toda a lógica customizada você mesmo!

 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 ]

Observe que a matriz final não está classificada, o que, é claro, não é motivo de preocupação aqui. Mas agora, vamos imaginar um cenário mais complicado: temos uma matriz de usuários que extraímos de algum lugar, mas queremos ter certeza de que contém apenas usuários únicos. Fácil com 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 } ] */

Neste exemplo, usamos o método uniqBy() para dizer a Lodash que queremos que os objetos sejam únicos na propriedade id . Em uma linha, expressamos o que poderia levar de 10 a 20 linhas e introduzimos mais escopo para bugs!

Há muito mais informações disponíveis sobre como tornar as coisas únicas no Lodash, e eu encorajo você a dar uma olhada na documentação.

Diferença de duas matrizes

União, diferença, etc., podem soar como termos que é melhor deixar para trás em palestras enfadonhas de ensino médio sobre Teoria dos Conjuntos, mas eles surgem com mais frequência do que na prática cotidiana. É comum ter uma lista e querer mesclar outra lista com ela ou querer descobrir quais elementos são exclusivos dela em comparação com outra lista; para esses cenários, a função de diferença é perfeita.

Olá, A. Tchau, B!

Vamos começar a jornada da diferença pegando um cenário simples: você recebeu uma lista de todos os IDs de usuário no sistema, bem como uma lista daqueles cujas contas estão ativas. Como você encontra os ids inativos? Simples, certo?

 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 ]

E se, como acontece em um ambiente mais realista, você tiver que trabalhar com uma matriz de objetos em vez de primitivos simples? Bem, Lodash tem um bom método differenceBy() para isso!

 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' } ]

Legal, certo ?!

Como diferença, existem outros métodos em Lodash para operações de conjunto comuns: união, interseção, etc.

Matrizes de nivelamento

A necessidade de nivelar matrizes surge com bastante frequência. Um caso de uso é que você recebeu uma resposta da API e precisa aplicar alguma combinação map() e filter() em uma lista complexa de objetos / matrizes aninhados para retirar, digamos, ids de usuário, e agora você fica com matrizes de matrizes. Aqui está um snippet de código que descreve esta situação:

 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);

Você consegue adivinhar como postPaidUserIds se parece agora? Dica: é nojento!

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

Agora, se você é uma pessoa sensata, não quer escrever uma lógica customizada para extrair os objetos de pedido e organizá-los em uma linha dentro de um array. Basta usar o método flatten() e desfrutar das 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' } ] */

Observe que flatten() apenas um nível de profundidade. Ou seja, se seus objetos estiverem presos em dois, três ou mais níveis de profundidade, flatten() eles irão desapontá-lo. Nesses casos, Lodash tem o método flattenDeep() , mas esteja avisado que a aplicação desse método em estruturas muito grandes pode desacelerar as coisas (como nos bastidores, há uma operação recursiva em funcionamento).

O objeto / array está vazio?

Graças a como valores e tipos “falsos” funcionam em JavaScript, às vezes algo tão simples como verificar se há vazio resulta em pavor existencial.

Como você verifica se um array está vazio? Você pode verificar se seu length é 0 ou não. Agora, como você verifica se um objeto está vazio? Bem ... espere um minuto! É aqui que surge aquela sensação desagradável, e aqueles exemplos de JavaScript contendo coisas como [] == false e {} == false começam a girar em nossas cabeças. Quando sob pressão para fornecer um recurso, minas terrestres como essas são a última coisa de que você precisa - elas tornarão seu código difícil de entender e introduzirão incerteza em seu conjunto de testes.

Trabalhar com dados perdidos

No mundo real, os dados nos ouvem; não importa o quanto queiramos, raramente é simples e lógico. Um exemplo típico é a ausência de objetos / matrizes nulos em uma grande estrutura de dados recebida como resposta da API.

Suponha que recebamos o seguinte objeto como uma resposta da API:

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

Conforme mostrado, geralmente obtemos um objeto de order na resposta da API, mas nem sempre é o caso. Então, e se tivermos algum código que depende desse objeto? Uma maneira seria codificar defensivamente, mas dependendo de quão aninhado está o objeto de order , logo estaríamos escrevendo um código muito feio se quisermos evitar erros de tempo de execução:

 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 ); }

Sim, muito feio de escrever, muito feio de ler, muito feio de manter e assim por diante. Felizmente, Lodash tem uma maneira direta de lidar com essas situações.

 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

Há também a opção fantástica de fornecer um valor padrão em vez de ficar undefined para itens ausentes:

 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

Não sei quanto a você, mas get() é uma daquelas coisas que me trazem lágrimas de felicidade. Não é nada chamativo. Não há sintaxe consultada ou opções para memorizar, mas veja a quantidade de sofrimento coletivo que isso pode aliviar!

Debouncing

Caso você não esteja familiarizado, debouncing é um tema comum no desenvolvimento de front-end. A ideia é que às vezes é benéfico iniciar uma ação não imediatamente, mas depois de algum tempo (geralmente, alguns milissegundos). O que isso significa? Aqui está um exemplo.

Imagine um site de comércio eletrônico com uma barra de pesquisa (bem, qualquer site / aplicativo da web hoje em dia!). Para uma melhor experiência do usuário, não queremos que o usuário tenha que pressionar enter (ou pior, pressionar o botão “pesquisar”) para mostrar sugestões / visualizações com base em seu termo de pesquisa. Mas a resposta óbvia é um pouco carregada: se adicionarmos um ouvinte de onChange() a onChange() para a barra de pesquisa e dispararmos uma chamada de API para cada pressionamento de tecla, teremos criado um pesadelo para nosso back-end; haveria muitas chamadas desnecessárias (por exemplo, se “escova de carpete branco” for pesquisada, haverá um total de 18 solicitações!) e quase todas elas serão irrelevantes porque a entrada do usuário não foi concluída.

A resposta está no debouncing, e a ideia é esta: não envie uma chamada API assim que o texto mudar. Aguarde algum tempo (digamos, 200 milissegundos) e, se nessa altura houver outro pressionamento de tecla, cancele a contagem de tempo anterior e comece a esperar novamente. Como resultado, é apenas quando o usuário faz uma pausa (seja porque está pensando ou porque terminou e espera alguma resposta) que enviamos uma solicitação de API ao back-end.

A estratégia geral que descrevi é complicada e não vou mergulhar na sincronização do gerenciamento e cancelamento do cronômetro; no entanto, o processo real de depuração é muito simples se você estiver usando o 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

Se você está pensando em setTimeout() eu teria feito o mesmo trabalho, bem, há mais! O debounce de Lodash vem com muitos recursos poderosos; por exemplo, você pode querer garantir que o debounce não seja indefinido. Ou seja, mesmo se houver um pressionamento de tecla toda vez que a função estiver prestes a ser disparada (cancelando assim o processo geral), você pode querer ter certeza de que a chamada de API seja feita de qualquer maneira após, digamos, dois segundos. Para isso, Lodash debounce debounce() tem a opção maxWait :

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

Verifique os documentos oficiais para um mergulho mais profundo. Eles estão cheios de coisas superimportantes!

Remova os valores de uma matriz

Não sei sobre você, mas odeio escrever código para remover itens de uma matriz. Primeiro, preciso obter o índice do item, verificar se o índice é realmente válido e, em caso afirmativo, chamar o método splice() e assim por diante. Nunca consigo me lembrar da sintaxe e, portanto, preciso pesquisar as coisas o tempo todo e, no final, fico com a sensação incômoda de que deixei algum bug estúpido se insinuar.

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

Observe duas coisas:

  1. A matriz original foi alterada no processo.
  2. O método pull() remove todas as instâncias, mesmo se houver duplicatas.

Existe outro método relacionado chamado pullAll() que aceita um array como o segundo parâmetro, tornando mais fácil remover vários itens de uma vez. Admitindo que poderíamos apenas usar pull() com um operador de propagação, mas lembre-se que Lodash veio em um momento em que o operador de propagação nem era uma proposta na linguagem!

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

Último índice de um elemento

O método nativo indexOf() do JavsScript é legal, exceto quando você estiver interessado em escanear o array na direção oposta! E mais uma vez, sim, você poderia simplesmente escrever um loop decrescente e encontrar o elemento, mas por que não usar uma técnica muito mais elegante?

Aqui está uma solução Lodash rápida usando o método lastIndexOf() :

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

Infelizmente, não há nenhuma variante desse método em que possamos pesquisar objetos complexos ou até mesmo passar uma função de pesquisa personalizada.

Fecho eclair. Descompactar!

A menos que você tenha trabalhado em Python, zip / unzip é um utilitário que você pode nunca notar ou imaginar em toda a sua carreira como desenvolvedor de JavaScript. E talvez por um bom motivo: raramente há o tipo de necessidade desesperada de zip / unzip como há de filter() , etc. No entanto, é um dos melhores utilitários menos conhecidos e pode ajudá-lo a criar código sucinto em algumas situações .

Ao contrário do que parece, zip / unzip não tem nada a ver com compressão. Em vez disso, é uma operação de agrupamento onde arrays do mesmo comprimento podem ser convertidos em um único array de arrays com elementos na mesma posição empacotados juntos ( zip() ) e de volta ( unzip() ). Sim, eu sei, está ficando nebuloso tentar se contentar com palavras, então vamos dar uma olhada em alguns códigos:

 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' ] ]

Os três arrays originais foram convertidos em um único com apenas dois arrays. E cada uma dessas novas matrizes representa um único animal com todos os seus em um só lugar. Assim, o índice 0 indica que tipo de animal é, o índice 1 indica o seu tamanho e o índice 2 indica o seu peso. Como resultado, agora é mais fácil trabalhar com os dados. Depois de aplicar todas as operações necessárias aos dados, você pode dividi-los novamente usando unzip() e enviá-los de volta à fonte original:

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

O utilitário zip / unzip não é algo que mudará sua vida durante a noite, mas mudará sua vida um dia!

Conclusão

(Coloquei todo o código-fonte usado neste artigo aqui para você experimentar diretamente do navegador!)

Os documentos Lodash estão repletos de exemplos e funções que irão simplesmente explodir sua mente. Em uma época em que o masoquismo parece estar crescendo no ecossistema JS, Lodash é como uma lufada de ar fresco, e eu recomendo fortemente que você use esta biblioteca em seus projetos!