10 fonctions Lodash importantes pour les développeurs JavaScript

Publié: 2021-10-05

Pour les développeurs JavaScript, Lodash n'a pas besoin d'être présenté. Cependant, la bibliothèque est vaste et semble souvent écrasante. Pas plus!

Lodash, Lodash, Lodash . . . par où commencer !

Il fut un temps où l'écosystème JavaScript était naissant ; cela pourrait être comparé au Far West ou à une jungle si vous voulez, où il se passait beaucoup de choses, mais il y avait très peu de réponses aux frustrations et à la productivité quotidiennes des développeurs.

Puis Lodash est entré en scène, et c'était comme une inondation qui a tout submergé. Des simples besoins quotidiens tels que le tri aux transformations complexes de la structure des données, Lodash a été chargé (même surchargé !) de fonctionnalités qui ont transformé la vie des développeurs JS en un pur bonheur.

Salut, Lodash !

Et où est Lodash aujourd'hui ? Eh bien, il a toujours tous les avantages qu'il offrait au départ, puis certains, mais il semble avoir perdu la tête dans la communauté JavaScript. Pourquoi? Je peux penser à plusieurs raisons :

  • Certaines fonctions de la bibliothèque Lodash étaient (et sont toujours) lentes lorsqu'elles sont appliquées à de grandes listes. Bien que cela n'ait jamais affecté 95% des projets, les développeurs influents des 5% restants ont donné mauvaise presse à Lodash et l'effet s'est répercuté sur la base.
  • Il y a une tendance dans l'écosystème JS (pourrait même dire la même chose à propos des gens du Golang) où l'orgueil est plus courant que nécessaire. Donc, s'appuyer sur quelque chose comme Lodash est considéré comme stupide et est abattu sur des forums comme StackOverflow lorsque les gens suggèrent de telles solutions (« Quoi ? ! Utiliser une bibliothèque entière pour quelque chose comme ça ? Je peux combiner filter() avec reduce() pour obtenir la même chose dans une fonction simple ! »).
  • Lodash est vieux. Au moins selon les normes JS. Il est sorti en 2012, donc au moment de la rédaction, cela fait presque dix ans. L'API est stable et peu de choses intéressantes peuvent être ajoutées chaque année (simplement parce que ce n'est pas nécessaire), ce qui génère de l'ennui pour le développeur JS surexcité moyen.

À mon avis, ne pas utiliser Lodash est une perte importante pour nos bases de code JavaScript. Il s'est avéré être des solutions élégantes et sans bug pour les problèmes quotidiens que nous rencontrons au travail, et son utilisation ne fera que rendre notre code plus lisible et maintenable.

Cela dit, plongeons-nous dans certaines des fonctions Lodash courantes (ou non !) et voyons à quel point cette bibliothèque est incroyablement utile et belle.

Cloner. . . profondément!

Étant donné que les objets sont transmis par référence en JavaScript, cela crée un casse-tête pour les développeurs lorsqu'ils souhaitent cloner quelque chose dans l'espoir que le nouvel ensemble de données est différent.

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

Remarquez comment, dans notre pure innocence et malgré nos bonnes intentions, le tableau des people origine a muté au cours du processus (la spécialisation d'Arnold est passée de C++ à JS ) - un coup dur pour l'intégrité du système logiciel sous-jacent ! En effet, nous avons besoin d'un moyen de faire une copie fidèle (en profondeur) du tableau d'origine.

Salut Dave, rencontre Dave !

Vous pouvez peut-être affirmer qu'il s'agit d'une manière « idiote » de coder en JS ; cependant, la réalité est un peu compliquée. Oui, nous avons le bel opérateur de déstructuration disponible, mais quiconque a essayé de déstructurer des objets et des tableaux complexes connaît la douleur. Ensuite, il y a l'idée d'utiliser la sérialisation et la désérialisation (peut-être JSON) pour réaliser une copie en profondeur, mais cela ne fait que rendre votre code plus compliqué pour le lecteur.

En revanche, regardez à quel point la solution est incroyablement élégante et concise lorsque Lodash s'habitue :

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

Remarquez comment le tableau people est intact après le clonage en profondeur (Arnold se spécialise toujours dans le C++ dans ce cas). Mais plus important encore, le code est simple à comprendre.

Supprimer les doublons d'un tableau

Supprimer les doublons d'un tableau ressemble à un excellent problème d'interview/tableau blanc (rappelez-vous, en cas de doute, jetez une hashmap sur le problème !). Et, bien sûr, vous pouvez toujours écrire une fonction personnalisée pour le faire, mais que se passe-t-il si vous rencontrez plusieurs scénarios différents dans lesquels rendre vos tableaux uniques ? Vous pouvez écrire plusieurs autres fonctions pour cela (et risquer de rencontrer des bogues subtils), ou vous pouvez simplement utiliser Lodash !

Notre premier exemple de baies uniques est plutôt trivial, mais il représente toujours la vitesse et la fiabilité que Lodash apporte à la table. Imaginez faire cela en écrivant vous-même toute la logique personnalisée !

 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 ]

Notez que le tableau final n'est pas trié, ce qui, bien sûr, n'est pas un problème ici. Mais maintenant, imaginons un scénario plus compliqué : nous avons un tableau d'utilisateurs que nous avons tiré de quelque part, mais nous voulons nous assurer qu'il ne contient que des utilisateurs uniques. Facile avec 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 } ] */

Dans cet exemple, nous avons utilisé la méthode uniqBy() pour indiquer à Lodash que nous voulons que les objets soient uniques sur la propriété id . Dans une ligne, nous avons exprimé ce qui aurait pu prendre 10 à 20 lignes et introduit plus de possibilités de bogues !

Il y a beaucoup plus de choses disponibles pour rendre les choses uniques dans Lodash, et je vous encourage à jeter un œil à la documentation.

Différence de deux tableaux

Union, différence, etc., peuvent sembler être des termes qu'il vaut mieux laisser de côté dans les cours ennuyeux du secondaire sur la théorie des ensembles, mais ils apparaissent le plus souvent dans la pratique quotidienne. Il est courant d'avoir une liste et de vouloir fusionner une autre liste avec elle ou de vouloir trouver quels éléments lui sont uniques par rapport à une autre liste ; pour ces scénarios, la fonction différence est parfaite.

Bonjour, A. Au revoir, B !

Commençons le voyage de la différence en prenant un scénario simple : vous avez reçu une liste de tous les identifiants d'utilisateur dans le système, ainsi qu'une liste de ceux dont les comptes sont actifs. Comment trouvez-vous les identifiants inactifs? Simple, non ?

 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 ]

Et si, comme cela se produit dans un cadre plus réaliste, vous deviez travailler avec un tableau d'objets au lieu de simples primitives ? Eh bien, Lodash a une belle méthode differenceBy() pour cela !

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

Sympa, non ?!

Comme la différence, il existe d'autres méthodes dans Lodash pour les opérations d'ensemble communes : union, intersection, etc.

Aplatissement des tableaux

Le besoin d'aplatir les tableaux se pose assez souvent. Un cas d'utilisation est que vous avez reçu une réponse de l'API et que vous devez appliquer un combo map() et filter() sur une liste complexe d'objets/tableaux imbriqués pour extraire, disons, les identifiants d'utilisateur, et il vous reste maintenant tableaux de tableaux. Voici un extrait de code décrivant cette situation :

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

Pouvez-vous deviner à quoi postPaidUserIds maintenant postPaidUserIds ? Indice : c'est dégoûtant !

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

Maintenant, si vous êtes une personne sensée, vous ne voulez pas écrire de logique personnalisée pour extraire les objets de commande et les disposer correctement dans une rangée à l'intérieur d'un tableau. Utilisez simplement la méthode flatten() et profitez des raisins :

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

Notez que flatten() ne va qu'à un niveau de profondeur. C'est-à-dire que si vos objets sont coincés à deux, trois niveaux ou plus de profondeur, flatten() ils vous décevront. Dans ces cas, Lodash a la méthode flattenDeep() , mais flattenDeep() que l'application de cette méthode sur de très grandes structures peut ralentir les choses (car en coulisse, il y a une opération récursive à l'œuvre).

L'objet/tableau est-il vide ?

Grâce au fonctionnement des valeurs et des types « faux » en JavaScript, parfois quelque chose d'aussi simple que la vérification de la vacuité entraîne une peur existentielle.

Comment vérifier si un tableau est vide ? Vous pouvez vérifier si sa length est 0 ou non. Maintenant, comment vérifier si un objet est vide ? Eh bien… attendez une minute ! C'est là que ce sentiment de malaise s'installe, et ces exemples JavaScript contenant des trucs comme [] == false et {} == false commencent à tourner autour de nos têtes. Lorsque vous êtes sous pression pour fournir une fonctionnalité, les mines terrestres comme celles-ci sont la dernière chose dont vous avez besoin - elles rendront votre code difficile à comprendre et elles introduiront de l'incertitude dans votre suite de tests.

Travailler avec des données manquantes

Dans le monde réel, les données nous écoutent ; peu importe à quel point nous le voulons, il est rarement rationalisé et sain d'esprit. Un exemple typique est l'absence d'objets/tableaux null dans une grande structure de données reçue en tant que réponse API.

Supposons que nous recevions l'objet suivant en tant que réponse API :

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

Comme indiqué, nous obtenons généralement un objet de order dans la réponse de l'API, mais ce n'est pas toujours le cas. Alors, que se passe-t-il si nous avons du code qui repose sur cet objet ? Une façon serait de coder de manière défensive, mais selon la façon dont l'objet de order est imbriqué, nous écririons bientôt du code très laid si nous souhaitons éviter les erreurs d'exécution :

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

Oui, très moche à écrire, très moche à lire, très moche à entretenir, etc. Heureusement, Lodash a un moyen simple de faire face à de telles situations.

 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

Il existe également l'option fantastique de fournir une valeur par défaut au lieu d'être undefined pour les éléments manquants :

 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

Je ne sais pas pour vous, mais get() est une de ces choses qui me font monter les larmes aux yeux. Ce n'est rien de tape-à-l'œil. Il n'y a pas de syntaxe consultée ni d'options à mémoriser, mais regardez la quantité de souffrance collective que cela peut soulager !

Anti-rebond

Au cas où vous ne le sauriez pas, l'anti-rebond est un thème courant dans le développement frontend. L'idée est que parfois il est avantageux de lancer une action non pas immédiatement mais après un certain temps (généralement, quelques millisecondes). Qu'est-ce que ça veut dire? Voici un exemple.

Imaginez un site Web de commerce électronique avec une barre de recherche (enfin, n'importe quel site Web/application Web de nos jours !). Pour une meilleure expérience utilisateur, nous ne voulons pas que l'utilisateur ait à appuyer sur Entrée (ou pire, appuyez sur le bouton « rechercher ») pour afficher des suggestions/aperçus en fonction de son terme de recherche. Mais la réponse évidente est un peu chargée : si nous ajoutions un écouteur d'événement à onChange() pour la barre de recherche et déclenchions un appel API pour chaque frappe, nous aurions créé un cauchemar pour notre backend ; il y aurait trop d'appels inutiles (par exemple, si « brosse à tapis blanc » est recherché, il y aura un total de 18 demandes !)

La réponse réside dans l'anti-rebond, et l'idée est la suivante : n'envoyez pas d'appel API dès que le texte change. Attendez un certain temps (disons 200 millisecondes) et si à ce moment-là il y a une autre frappe, annulez le décompte de temps précédent et recommencez à attendre. En conséquence, ce n'est que lorsque l'utilisateur fait une pause (soit parce qu'il réfléchit, soit parce qu'il a terminé et qu'il attend une réponse) que nous envoyons une requête API au backend.

La stratégie globale que j'ai décrite est compliquée, et je ne vais pas plonger dans la synchronisation de la gestion et de l'annulation du timer ; cependant, le processus d'anti-rebond est très simple si vous utilisez 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 vous pensez à setTimeout() j'aurais fait le même travail, eh bien, il y a plus ! L'anti-rebond de Lodash est livré avec de nombreuses fonctionnalités puissantes ; par exemple, vous voudrez peut-être vous assurer que l'anti-rebond n'est pas indéfini. C'est-à-dire que même s'il y a une frappe à chaque fois que la fonction est sur le point de se déclencher (annulant ainsi le processus global), vous voudrez peut-être vous assurer que l'appel d'API est quand même effectué après, disons, deux secondes. Pour cela, Lodash debounce debounce() a l'option maxWait :

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

Consultez les documents officiels pour une plongée plus approfondie. Ils sont pleins de trucs super importants !

Supprimer des valeurs d'un tableau

Je ne sais pas pour vous, mais je déteste écrire du code pour supprimer des éléments d'un tableau. Tout d'abord, je dois obtenir l'index de l'élément, vérifier si l'index est réellement valide et si c'est le cas, appeler la méthode splice() , et ainsi de suite. Je ne me souviens jamais de la syntaxe et j'ai donc besoin de chercher des choses tout le temps, et à la fin, j'ai le sentiment persistant d'avoir laissé un bug stupide s'infiltrer.

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

Veuillez noter deux choses :

  1. Le tableau d'origine a été modifié au cours du processus.
  2. La méthode pull() supprime toutes les instances, même s'il y a des doublons.

Il existe une autre méthode connexe appelée pullAll() qui accepte un tableau comme deuxième paramètre, ce qui facilite la suppression de plusieurs éléments à la fois. Certes, nous pouvions simplement utiliser pull() avec un opérateur de propagation, mais rappelez-vous que Lodash est arrivé à une époque où l'opérateur de propagation n'était même pas une proposition dans le langage !

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

Dernier index d'un élément

La méthode native indexOf() est cool, sauf lorsque vous souhaitez analyser le tableau dans la direction opposée ! Et encore une fois, oui, vous pouvez simplement écrire une boucle décrémentante et trouver l'élément, mais pourquoi ne pas utiliser une technique beaucoup plus élégante ?

Voici une solution Lodash rapide utilisant la méthode lastIndexOf() :

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

Malheureusement, il n'y a pas de variante de cette méthode où nous pouvons rechercher des objets complexes ou même passer une fonction de recherche personnalisée.

Zipper. Décompressez !

À moins que vous n'ayez travaillé en Python, zip/unzip est un utilitaire que vous ne remarquerez ou n'imaginerez peut-être jamais de toute votre carrière de développeur JavaScript. Et peut-être pour une bonne raison : il y a rarement le genre de besoin désespéré de zip/unzip comme il y en a pour filter() , etc. Cependant, c'est l'un des meilleurs utilitaires les moins connus et peut vous aider à créer un code succinct dans certaines situations .

Contrairement à ce que cela peut paraître, zip/unzip n'a rien à voir avec la compression. Au lieu de cela, il s'agit d'une opération de regroupement où des tableaux de même longueur peuvent être convertis en un seul tableau de tableaux avec des éléments à la même position regroupés ( zip() ) et unzip() ). Ouais, je sais, ça devient flou d'essayer de se débrouiller avec des mots, alors regardons un peu de code :

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

Les trois tableaux d'origine ont été convertis en un seul avec deux tableaux seulement. Et chacun de ces nouveaux tableaux représente un seul animal avec tous ses éléments en un seul endroit. Ainsi, l'index 0 nous indique de quel type d'animal il s'agit, l'index 1 nous indique sa taille et l'index 2 nous indique son poids. En conséquence, les données sont désormais plus faciles à utiliser. Une fois que vous avez appliqué les opérations dont vous avez besoin sur les données, vous pouvez les décomposer à nouveau en utilisant unzip() et les renvoyer à la source d'origine :

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

L'utilitaire zip/unzip n'est pas quelque chose qui va changer votre vie du jour au lendemain, mais il changera votre vie un jour !

Conclusion

(J'ai mis tout le code source utilisé dans cet article ici pour que vous puissiez l'essayer directement depuis le navigateur !)

Les documents Lodash regorgent d'exemples et de fonctions qui vous épateront. À une époque où le masochisme semble augmenter dans l'écosystème JS, Lodash est comme une bouffée d'air frais, et je vous recommande fortement d'utiliser cette bibliothèque dans vos projets !