10 funcții Lodash importante pentru dezvoltatorii JavaScript

Pentru dezvoltatorii JavaScript, Lodash nu are nevoie de prezentare. Cu toate acestea, biblioteca este vastă și adesea se simte copleșitoare. Nu mai!

Lodash, Lodash, Lodash . . . de unde sa incep! 🤔

A fost o vreme când ecosistemul JavaScript era în curs de dezvoltare; ar putea fi comparat cu vestul sălbatic sau cu o junglă, dacă vreți, unde se întâmplau multe, dar au existat foarte puține răspunsuri pentru frustrările de zi cu zi ale dezvoltatorilor și productivitatea.

Apoi Lodash a intrat în scenă și s-a simțit ca o inundație care a scufundat totul. Chiar de la simple nevoi cotidiene, cum ar fi sortarea, până la transformări complexe ale structurii de date, Lodash a venit încărcat (supraîncărcat, chiar!) cu funcționalități care au transformat viața dezvoltatorilor JS într-o pură fericire.

Bună, Lodash!

Și unde este Lodash astăzi? Ei bine, încă mai are toate bunătățile pe care le-a oferit inițial, și apoi câteva, dar se pare că și-a pierdut cota minții în comunitatea JavaScript. De ce? Mă gândesc la câteva motive:

  • Unele funcții din biblioteca Lodash au fost (și sunt încă) lente atunci când sunt aplicate listelor mari. Deși acest lucru nu ar fi afectat niciodată 95% dintre proiectele de acolo, dezvoltatorii influenți din restul de 5% i-au dat lui Lodash o presă proastă, iar efectul a căzut în cascadă la nivel de bază.
  • Există o tendință în ecosistemul JS (s-ar putea să spună chiar același lucru despre oamenii Golang) în care orgoliul este mai frecvent decât este necesar. Deci, bazarea pe ceva de genul Lodash este văzută ca o prostie și este doborât pe forumuri precum StackOverflow atunci când oamenii sugerează astfel de soluții („Ce?! Folosesc o bibliotecă întreagă pentru așa ceva? Pot combina filter() cu reduce() pentru a realiza același lucru într-o funcție simplă!”).
  • Lodash este bătrân. Cel puțin după standardele JS. A ieșit în 2012, așa că, în momentul scrierii, au trecut aproape zece ani. API-ul a fost stabil și nu pot fi adăugate multe lucruri interesante în fiecare an (pur și simplu pentru că nu este nevoie), ceea ce generează plictiseală pentru dezvoltatorul JS mediu supraexcitat.

În opinia mea, neutilizarea Lodash este o pierdere semnificativă pentru bazele noastre de coduri JavaScript. S-a dovedit a fi soluții elegante și lipsite de erori pentru problemele de zi cu zi cu care ne confruntăm la locul de muncă, iar utilizarea lui va face codul nostru mai ușor de citit și mai ușor de întreținut.

Acestea fiind spuse, haideți să ne aprofundăm în unele dintre funcțiile comune (sau nu!) Lodash și să vedem cât de incredibil de utilă și frumoasă este această bibliotecă.

Clonează. . . profund!

Deoarece obiectele sunt transmise prin referință în JavaScript, creează o bătaie de cap pentru dezvoltatori atunci când doresc să cloneze ceva cu speranța că noul set de date este diferit.

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

Observați cum, în pura noastră inocență și în ciuda bunelor noastre intenții, matricea originală de oameni a suferit mutații în proces (specializarea lui Arnold s-a schimbat de la C++ la JS) – o lovitură majoră pentru integritatea sistemului software de bază! Într-adevăr, avem nevoie de o modalitate de a face o copie adevărată (profundă) a matricei originale.

Bună Dave, cunoaște-te pe Dave!

Poate puteți argumenta că acesta este un mod „prost” de a codifica în JS; cu toate acestea, realitatea este puțin complicată. Da, avem la dispoziție minunatul operator de destructurare, dar oricine a încercat să destructureze obiecte și matrice complexe cunoaște durerea. Apoi, există ideea de a utiliza serializarea și deserializarea (poate JSON) pentru a realiza o copiere profundă, dar doar face codul dvs. mai dezordonat pentru cititor.

În schimb, uite cât de uimitor de elegantă și concisă este soluția atunci când Lodash se obișnuiește:

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

Observați cum matricea de oameni este neatinsă după clonarea profundă (Arnold este încă specializat în C++ în acest caz). Dar, mai important, codul este ușor de înțeles.

  Cele mai bune 6 platforme de auto-publicare care vă fac munca acolo

Eliminați duplicatele dintr-o matrice

Eliminarea duplicatelor dintr-o matrice sună ca o problemă excelentă de interviu/tablă albă (amintiți-vă, dacă aveți îndoieli, aruncați o hartă hash la problemă!). Și, desigur, puteți oricând să scrieți o funcție personalizată pentru a face asta, dar ce se întâmplă dacă întâlniți mai multe scenarii diferite în care să vă faceți matricele unice? Ați putea scrie alte câteva funcții pentru asta (și riscați să întâlniți erori subtile), sau puteți utiliza doar Lodash!

Primul nostru exemplu de matrice unice este destul de banal, dar încă reprezintă viteza și fiabilitatea pe care Lodash le aduce la masă. Imaginează-ți că faci asta scriind singur toată logica personalizată!

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 ]

Observați că matricea finală nu este sortată, ceea ce, desigur, nu este de nicio preocupare aici. Dar acum, să ne imaginăm un scenariu mai complicat: avem o serie de utilizatori pe care i-am extras de undeva, dar vrem să ne asigurăm că conține doar utilizatori unici. Ușor cu 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 }
]
*/

În acest exemplu, am folosit metoda uniqBy() pentru a-i spune lui Lodash că dorim ca obiectele să fie unice pe proprietatea id. Într-o singură linie, am exprimat ceea ce ar fi putut dura 10-20 de rânduri și am introdus mai mult spațiu pentru erori!

Există mult mai multe lucruri disponibile pentru a face lucrurile unice în Lodash și vă încurajez să aruncați o privire la docs.

Diferența a două matrice

Unire, diferență, etc., ar putea suna ca termeni cel mai bine lăsați în urmă în prelegerile plictisitoare ale liceului despre Teoria seturilor, dar apar cel mai des în practica de zi cu zi. Este obișnuit să aveți o listă și să doriți să îmbinați o altă listă cu ea sau să doriți să găsiți ce elemente sunt unice pentru ea în comparație cu o altă listă; pentru aceste scenarii, funcția de diferență este perfectă.

Bună, A. La revedere, B!

Să începem călătoria diferenței luând un scenariu simplu: ați primit o listă cu toate ID-urile de utilizator din sistem, precum și o listă a celor ale căror conturi sunt active. Cum găsiți ID-urile inactive? Simplu, nu?

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 ]

Și ce se întâmplă dacă, așa cum se întâmplă într-un cadru mai realist, trebuie să lucrați cu o serie de obiecte în loc de primitive simple? Ei bine, Lodash are o metodă frumoasă differenceBy() pentru asta!

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

Frumos, nu?!

Ca și diferență, există și alte metode în Lodash pentru operații obișnuite de set: unire, intersecție etc.

Aplatizarea matricelor

Necesitatea de a aplatiza matrice apare destul de des. Un caz de utilizare este că ați primit un răspuns API și trebuie să aplicați un combo map() și filter() pe o listă complexă de obiecte/matrice imbricate pentru a scoate, să zicem, ID-uri de utilizator, iar acum rămâneți cu matrice de matrice. Iată un fragment de cod care descrie această situație:

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

Puteți ghici cum arată acum postPaidUserIds? Sugestie: este dezgustător!

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

Acum, dacă ești o persoană sensibilă, nu vrei să scrii o logică personalizată pentru a extrage obiectele de comandă și a le așeza frumos într-un rând în interiorul unui tablou. Folosește doar metoda flatten() și bucură-te de struguri:

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

Rețineți că flatten() merge doar cu un nivel adânc. Adică, dacă obiectele tale sunt blocate la două, trei sau mai multe niveluri adâncime, flatten() te vor dezamăgi. În aceste cazuri, Lodash are metoda flattenDeep(), dar fiți avertizat că aplicarea acestei metode pe structuri foarte mari poate încetini lucrurile (deoarece în culise, există o operațiune recursivă).

  Cum să eliminați caracterul de subliniere final al unei adrese URL în Chrome

Obiectul/matricea este goală?

Datorită modului în care valorile și tipurile „false” funcționează în JavaScript, uneori ceva la fel de simplu precum verificarea golului duce la o teamă existențială.

Cum verifici dacă o matrice este goală? Puteți verifica dacă lungimea sa este 0 sau nu. Acum, cum verifici dacă un obiect este gol? Ei bine… stai putin! Aici se instalează acel sentiment neplăcut și acele exemple JavaScript care conțin lucruri precum [] == false și {} == false încep să ne învârtească capetele. Când sunteți sub presiune pentru a oferi o funcție, minele de tipul acestea sunt ultimul lucru de care aveți nevoie – vă vor face codul greu de înțeles și vor introduce incertitudine în suita dvs. de testare.

Lucrul cu datele lipsă

În lumea reală, datele ne ascultă; oricât de mult ne-am dori acest lucru, rareori este raționalizat și sănătos. Un exemplu tipic este lipsa obiectelor/matricelor nule într-o structură mare de date primită ca răspuns API.

Să presupunem că am primit următorul obiect ca răspuns API:

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

După cum se arată, primim, în general, un obiect de comandă în răspunsul de la API, dar nu este întotdeauna cazul. Deci, ce se întâmplă dacă avem un cod care se bazează pe acest obiect? O modalitate ar fi să codificăm în mod defensiv, dar în funcție de cât de imbricat este obiectul de comandă, vom scrie în curând cod foarte urât dacă dorim să evităm erorile de rulare:

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

🤢🤢 Da, foarte urât de scris, foarte urât de citit, foarte urât de întreținut și așa mai departe. Din fericire, Lodash are un mod simplu de a face față unor astfel de situații.

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

Există, de asemenea, opțiunea fantastică de a furniza o valoare implicită în loc să devină nedefinită pentru lucrurile lipsă:

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

Nu știu despre tine, dar get() este unul dintre acele lucruri care îmi aduc lacrimi de fericire în ochi. Nu este nimic strigător. Nu există o sintaxă sau opțiuni consultate de memorat, dar uită-te la cantitatea de suferință colectivă pe care o poate atenua! 😇

Debotant

În cazul în care nu sunteți familiarizat, debouncing este o temă comună în dezvoltarea frontend. Ideea este că uneori este benefic să lansezi o acțiune nu imediat, ci după un timp (în general, câteva milisecunde). Ce inseamna asta? Iată un exemplu.

Imaginați-vă un site de comerț electronic cu o bară de căutare (ei bine, orice site web/aplicație web în zilele noastre!). Pentru o UX mai bună, nu dorim ca utilizatorul să fie nevoit să apese Enter (sau, mai rău, să apese butonul „căutare”) pentru a afișa sugestii/previzualizări pe baza termenului său de căutare. Dar răspunsul evident este puțin încărcat: dacă adăugăm un ascultător de evenimente la onChange() pentru bara de căutare și lansăm un apel API pentru fiecare apăsare a tastei, am fi creat un coșmar pentru backend-ul nostru; ar fi prea multe apeluri inutile (de exemplu, dacă se caută „perie albă pentru covor”, vor fi în total 18 solicitări!) și aproape toate acestea vor fi irelevante deoarece introducerea utilizatorului nu s-a terminat.

Răspunsul constă în debouncing, iar ideea este următoarea: nu trimiteți un apel API imediat ce textul se schimbă. Așteptați ceva timp (să zicem, 200 de milisecunde) și dacă până în acel moment există o altă apăsare a tastei, anulați contorizarea timpului anterior și începeți din nou să așteptați. Drept urmare, numai atunci când utilizatorul se întrerupe (fie pentru că se gândește, fie pentru că a terminat și așteaptă un răspuns) trimitem o solicitare API către backend.

Strategia generală pe care am descris-o este complicată și nu mă voi scufunda în sincronizarea gestionării și anulării cronometrului; cu toate acestea, procesul real de eliminare este foarte simplu dacă utilizați 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

Dacă te gândești că setTimeout() aș fi făcut aceeași treabă, ei bine, sunt mai multe! Rebotarea lui Lodash vine cu multe caracteristici puternice; de exemplu, ați putea dori să vă asigurați că retragerea nu este nelimitată. Adică, chiar dacă există o apăsare a tastei de fiecare dată când funcția este pe cale să se declanșeze (anulând astfel întregul proces), poate doriți să vă asigurați că apelul API este efectuat oricum după, să zicem, două secunde. Pentru aceasta, Lodash debounce() are opțiunea maxWait:

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

Verificați oficialul docs pentru o scufundare mai adâncă. Sunt pline de lucruri super importante!

  Cum să arunci oameni în Gang Beasts

Eliminați valori dintr-o matrice

Nu știu despre tine, dar urăsc să scriu cod pentru eliminarea elementelor dintr-o matrice. În primul rând, trebuie să obțin indexul articolului, să verific dacă indexul este de fapt valid și, dacă da, să apelez metoda splice() și așa mai departe. Nu-mi amintesc niciodată sintaxa și, prin urmare, trebuie să caut lucrurile tot timpul, iar la sfârșitul ei, am rămas cu sentimentul sâcâitor că am lăsat să se strecoare un bug stupid.

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

Vă rugăm să rețineți două lucruri:

  • Matricea originală a fost schimbată în acest proces.
  • Metoda pull() elimină toate instanțele, chiar dacă există duplicate.
  • Există o altă metodă asociată numită pullAll() care acceptă o matrice ca al doilea parametru, facilitând eliminarea mai multor elemente simultan. Desigur că am putea folosi doar pull() cu un operator de împrăștiere, dar amintiți-vă că Lodash a venit într-un moment în care operatorul de împrăștiere nu era nici măcar o propunere în limbă!

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

    Ultimul index al unui element

    Metoda nativă indexOf() a lui JavsScript este grozavă, cu excepția cazului în care sunteți interesat să scanați matricea din direcția opusă! Și încă o dată, da, ați putea să scrieți o buclă de decrementare și să găsiți elementul, dar de ce să nu folosiți o tehnică mult mai elegantă?

    Iată o soluție rapidă Lodash folosind metoda lastIndexOf():

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

    Din păcate, nu există nicio variantă a acestei metode în care să putem căuta obiecte complexe sau chiar să trecem o funcție de căutare personalizată.

    Zip. Dezarhivați!

    Cu excepția cazului în care ai lucrat în Python, zip/unzip este un utilitar pe care s-ar putea să nu-l observi sau să-l imaginezi niciodată în întreaga ta carieră de dezvoltator JavaScript. Și poate dintr-un motiv întemeiat: rareori există nevoia disperată de zip/unzip ca și pentru filter(), etc. Cu toate acestea, este unul dintre cele mai bune utilitare mai puțin cunoscute din jur și vă poate ajuta să creați cod succint în anumite situații .

    Spre deosebire de ceea ce sună, zip/unzip nu are nimic de-a face cu compresia. În schimb, este o operațiune de grupare în care matricele de aceeași lungime pot fi convertite într-o singură matrice de matrice cu elemente în aceeași poziție împachetate împreună (zip()) și înapoi (unzip()). Da, știu, devine neclar să încerci să te descurci cu cuvintele, așa că hai să ne uităm la un cod:

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

    Cele trei matrice originale au fost convertite într-una singură cu doar două matrice. Și fiecare dintre aceste noi matrice reprezintă un singur animal cu toate acestea într-un singur loc. Deci, indicele 0 ne spune tipul de animal care este, indicele 1 ne spune dimensiunea lui, iar indicele 2 ne spune greutatea lui. Ca rezultat, acum este mai ușor de lucrat cu datele. După ce ați aplicat orice operațiuni de care aveți nevoie asupra datelor, puteți să le despărțiți din nou folosind unzip() și să le trimiteți înapoi la sursa originală:

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

    Utilitarul zip/unzip nu este ceva care îți va schimba viața peste noapte, dar îți va schimba viața într-o zi!

    Concluzie 👨‍🏫

    (Am pus tot codul sursă folosit în acest articol Aici pentru ca tu să încerci direct din browser!)

    Lodash docs sunt pline de exemple și funcții care vă vor uimi mintea. Într-o zi și o epocă în care masochismul pare să crească în ecosistemul JS, Lodash este ca o gură de aer proaspăt și vă recomand cu căldură să utilizați această bibliotecă în proiectele voastre!