10 viktige Lodash-funksjoner for JavaScript-utviklere

For JavaScript-utviklere trenger Lodash ingen introduksjon. Biblioteket er imidlertid stort og føles ofte overveldende. Ikke nå lenger!

Lodash, Lodash, Lodash . . . hvor skal jeg begynne! 🤔

Det var en tid da JavaScript-økosystemet var begynnende; det kan sammenlignes med det ville vesten eller en jungel om du vil, hvor mye foregikk, men det var svært få svar på hverdagslige utviklerfrustrasjoner og produktivitet.

Deretter Lodash kom inn på scenen, og det føltes som en flom som senket alt. Helt fra enkle hverdagsbehov som sortering til komplekse datastrukturtransformasjoner, kom Lodash lastet (overbelastet, til og med!) med funksjonalitet som gjorde JS-utviklernes liv til ren lykke.

Hei, Lodash!

Og hvor er Lodash i dag? Vel, den har fortsatt alle godbitene den tilbød i utgangspunktet, og litt til, men den ser ut til å ha mistet andelen i JavaScript-fellesskapet. Hvorfor? Jeg kan tenke på noen grunner:

  • Noen funksjoner i Lodash-biblioteket var (og er fortsatt) trege når de ble brukt på store lister. Selv om dette aldri ville ha påvirket 95 % av prosjektene der ute, ga innflytelsesrike utviklere fra de resterende 5 % Lodash en dårlig presse og effekten fosset ned i grasrota.
  • Det er en trend i JS-økosystemet (kan til og med si det samme om Golang-folket) der hybris er mer vanlig enn nødvendig. Så å stole på noe som Lodash blir sett på som dumt og blir skutt ned på fora som StackOverflow når folk foreslår slike løsninger («Hva?! Bruk et helt bibliotek til noe slikt? Jeg kan kombinere filter() med reduce() for å oppnå det samme i en enkel funksjon!”).
  • Lodash er gammel. I hvert fall etter JS-standarder. Den kom ut i 2012, så i skrivende stund har det gått nesten ti år. API-en har vært stabil, og ikke mye spennende kan legges til hvert år (rett og slett fordi det ikke er nødvendig), noe som genererer kjedsomhet for den gjennomsnittlige overbegeistrede JS-utvikleren.

Etter min mening er det å ikke bruke Lodash et betydelig tap for JavaScript-kodebasene våre. Den har bevist feilfrie og elegante løsninger for hverdagsproblemer vi møter på jobben, og bruk av den vil bare gjøre koden vår mer lesbar og vedlikeholdbar.

Med det sagt, la oss dykke ned i noen av de vanlige (eller ikke!) Lodash-funksjonene og se hvor utrolig nyttig og vakkert dette biblioteket er.

Klone . . . dypt!

Siden objekter sendes ved referanse i JavaScript, skaper det en hodepine for utviklere når de ønsker å klone noe med håp om at det nye datasettet er annerledes.

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

Legg merke til hvordan i vår rene uskyld og til tross for våre gode intensjoner, muterte den opprinnelige folkegruppen i prosessen (Arnolds spesialisering endret seg fra C++ til JS) – et stort slag for integriteten til det underliggende programvaresystemet! Vi trenger faktisk en måte å lage en sann (dyp) kopi av den originale matrisen på.

Hei Dave, møt Dave!

Du kan kanskje argumentere for at dette er en «tullete» måte å kode på i JS; men virkeligheten er litt komplisert. Ja, vi har den nydelige destruktureringsoperatøren tilgjengelig, men alle som har prøvd å destrukturere komplekse objekter og arrays kjenner smerten. Så er det ideen om å bruke serialisering og de-serialisering (kanskje JSON) for å oppnå dyp kopiering, men det gjør bare koden din mer rotete for leseren.

Derimot, se hvor utrolig elegant og konsis løsningen er når Lodash blir brukt:

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

Legg merke til hvordan personarrayen er urørt etter dyp kloning (Arnold spesialiserer seg fortsatt på C++ i dette tilfellet). Men enda viktigere, koden er enkel å forstå.

  9 Imponerende Persona Generator for markedsførere

Fjern duplikater fra en matrise

Å fjerne duplikater fra en array høres ut som et utmerket intervju/whiteboard-problem (husk, når du er i tvil, kast et hashmap på problemet!). Og selvfølgelig kan du alltid skrive en egendefinert funksjon for å gjøre det, men hva om du møter flere forskjellige scenarier for å gjøre matrisene dine unike? Du kan skrive flere andre funksjoner for det (og risikere å støte på subtile feil), eller du kan bare bruke Lodash!

Vårt første eksempel på unike arrays er ganske trivielt, men det representerer fortsatt hastigheten og påliteligheten som Lodash bringer til bordet. Tenk deg å gjøre dette ved å skrive all den tilpassede logikken selv!

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 ]

Legg merke til at den endelige matrisen ikke er sortert, noe som selvfølgelig ikke er av betydning her. Men nå, la oss forestille oss et mer komplisert scenario: vi har en rekke brukere vi hentet fra et sted, men vi vil sørge for at det bare inneholder unike brukere. Enkelt med 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 }
]
*/

I dette eksemplet brukte vi uniqBy()-metoden for å fortelle Lodash at vi vil at objektene skal være unike på id-egenskapen. På én linje uttrykte vi hva som kunne ha tatt 10-20 linjer og introduserte mer muligheter for feil!

Det er mye mer tilgjengelig rundt å gjøre ting unike i Lodash, og jeg oppfordrer deg til å ta en titt på dokumenter.

Forskjellen på to matriser

Union, forskjell, etc., kan høres ut som begreper som det er best å legge igjen i kjedelige videregående forelesninger om settteori, men de dukker opp oftere enn ikke i hverdagen. Det er vanlig å ha en liste og ønsker å slå sammen en annen liste med den eller ønsker å finne hvilke elementer som er unike for den sammenlignet med en annen liste; for disse scenariene er forskjellsfunksjonen perfekt.

Hei, A. Farvel, B!

La oss begynne reisen til forskjell ved å ta et enkelt scenario: du har mottatt en liste over alle bruker-ID-ene i systemet, samt en liste over de som har kontoer aktive. Hvordan finner du de inaktive ID-ene? Enkelt, ikke sant?

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 ]

Og hva om, som det skjer i en mer realistisk setting, må du jobbe med en rekke objekter i stedet for enkle primitiver? Vel, Lodash har en fin differenceBy()-metode for dette!

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

Greit, ikke sant?!

Som forskjell er det andre metoder i Lodash for vanlige settoperasjoner: union, kryss, etc.

Flate ut arrays

Behovet for å flate ut matriser oppstår ganske ofte. Et brukstilfelle er at du har mottatt et API-svar og må bruke en kombinasjon av map() og filter() på en kompleks liste over nestede objekter/arrays for å plukke ut, for eksempel, bruker-IDer, og nå sitter du igjen med arrays av arrays. Her er en kodebit som viser denne situasjonen:

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

Kan du gjette hvordan postPaidUserIds ser ut nå? Hint: det er ekkelt!

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

Nå, hvis du er en fornuftig person, vil du ikke skrive tilpasset logikk for å trekke ut rekkefølgeobjektene og legge dem ut på en rekke i en matrise. Bare bruk flatten()-metoden og nyt druene:

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

Legg merke til at flatten() bare går ett nivå dypt. Det vil si at hvis objektene dine sitter fast to, tre eller flere nivåer dypt, flaten() vil de skuffe deg. I disse tilfellene har Lodash flattenDeep()-metoden, men vær advart om at bruk av denne metoden på veldig store strukturer kan bremse ting (ettersom bak kulissene er det en rekursiv operasjon på jobb).

  Slik sletter du meldinger fra iCloud

Er objektet/matrisen tom?

Takket være hvordan «falske» verdier og typer fungerer i JavaScript, resulterer noen ganger noe så enkelt som å se etter tomhet i eksistensiell frykt.

Hvordan sjekker du om en matrise er tom? Du kan sjekke om lengden er 0 eller ikke. Nå, hvordan sjekker du om et objekt er tomt? Vel … vent litt! Det er her den urolige følelsen setter inn, og de JavaScript-eksemplene som inneholder ting som [] == falsk og {} == falsk begynner å sirkle rundt hodet vårt. Når du er under press for å levere en funksjon, er landminer som disse det siste du trenger – de vil gjøre koden din vanskelig å forstå, og de vil introdusere usikkerhet i testpakken din.

Arbeider med manglende data

I den virkelige verden lytter data til oss; uansett hvor gjerne vi ønsker det, er det sjelden strømlinjeformet og fornuftig. Et typisk eksempel er manglende null-objekter/matriser i en stor datastruktur mottatt som API-svar.

Anta at vi mottok følgende objekt som et API-svar:

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

Som vist får vi vanligvis et ordreobjekt i svaret fra API, men det er ikke alltid tilfelle. Så, hva om vi har en kode som er avhengig av dette objektet? En måte ville være å kode defensivt, men avhengig av hvor nestet ordreobjektet er, vil vi snart skrive veldig stygg kode hvis vi ønsker å unngå kjøretidsfeil:

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

🤢🤢 Ja, veldig stygg å skrive, veldig stygg å lese, veldig stygg å vedlikeholde, og så videre. Heldigvis har Lodash en grei måte å håndtere slike situasjoner på.

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

Det er også det fantastiske alternativet å gi en standardverdi i stedet for å bli udefinert for manglende ting:

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

Jeg vet ikke med deg, men get() er en av de tingene som bringer lykketårer i øynene mine. Det er ikke noe prangende. Det er ingen konsultert syntaks eller alternativer å huske, men se på hvor mye kollektiv lidelse det kan lindre! 😇

Avhopping

I tilfelle du ikke er kjent, er debouncing et vanlig tema i frontend-utvikling. Tanken er at noen ganger er det fordelaktig å starte en handling ikke umiddelbart, men etter en tid (vanligvis noen få millisekunder). Hva betyr det? Her er et eksempel.

Se for deg et e-handelsnettsted med en søkefelt (vel, hvilken som helst nettside/nettapp i disse dager!). For bedre brukeropplevelse ønsker vi ikke at brukeren må trykke enter (eller enda verre, trykke på «søk»-knappen) for å vise forslag/forhåndsvisninger basert på søkeordet deres. Men det åpenbare svaret er litt lastet: hvis vi legger til en hendelseslytter til onChange() for søkefeltet og fyrer av et API-kall for hvert tastetrykk, ville vi ha skapt et mareritt for vår backend; det ville være for mange unødvendige oppringninger (hvis for eksempel «hvit teppebørste» søkes, vil det være totalt 18 forespørsler!) og nesten alle disse vil være irrelevante fordi brukerinndataene ikke er fullført.

Svaret ligger i debouncing, og ideen er denne: ikke send et API-kall så snart teksten endres. Vent en stund (f.eks. 200 millisekunder), og hvis det er et nytt tastetrykk innen den tid, kan du avbryte den tidligere tidstellingen og begynne å vente igjen. Som et resultat er det bare når brukeren tar pause (enten fordi de tenker eller fordi de er ferdige og de forventer noe svar) at vi sender en API-forespørsel til backend.

Den overordnede strategien jeg beskrev er komplisert, og jeg vil ikke dykke ned i synkroniseringen av tidsstyring og kansellering; Imidlertid er selve debouncingsprosessen veldig enkel hvis du bruker 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

Hvis du tenker setTimeout() ville jeg ha gjort den samme jobben, vel, det er mer! Lodashs debounce kommer med mange kraftige funksjoner; for eksempel kan det være lurt å sørge for at avvisningen ikke er ubestemt. Det vil si, selv om det er et tastetrykk hver gang funksjonen er i ferd med å utløses (og dermed avbryter den generelle prosessen), kan det være lurt å sørge for at API-kallet foretas uansett etter for eksempel to sekunder. For dette har Lodash debounce() alternativet maxWait:

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

Sjekk ut den offisielle dokumenter for et dypere dykk. De er fulle av superviktige ting!

  Beste Python-biblioteker for dataforskere

Fjern verdier fra en matrise

Jeg vet ikke med deg, men jeg hater å skrive kode for å fjerne elementer fra en matrise. Først må jeg hente elementets indeks, sjekke om indeksen faktisk er gyldig og i så fall kalle splice()-metoden, og så videre. Jeg kan aldri huske syntaksen og må derfor slå opp ting hele tiden, og på slutten av det sitter jeg igjen med den gnagende følelsen av at jeg har latt en eller annen dum feil krype inn.

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

Vær oppmerksom på to ting:

  • Den opprinnelige matrisen ble endret i prosessen.
  • Pull()-metoden fjerner alle forekomster, selv om det er duplikater.
  • Det er en annen relatert metode kalt pullAll() som aksepterer en matrise som den andre parameteren, noe som gjør det lettere å fjerne flere elementer samtidig. Gitt at vi bare kunne bruke pull() med en spread-operator, men husk at Lodash kom på et tidspunkt da spread-operatoren ikke engang var et forslag på språket!

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

    Siste indeks for et element

    JavsScripts native indexOf()-metode er kul, bortsett fra når du er interessert i å skanne arrayet fra motsatt retning! Og igjen, ja, du kan bare skrive en dekrementerende løkke og finne elementet, men hvorfor ikke bruke mye mer elegant teknikk?

    Her er en rask Lodash-løsning som bruker lastIndexOf()-metoden:

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

    Dessverre er det ingen variant av denne metoden der vi kan slå opp komplekse objekter eller til og med sende en egendefinert oppslagsfunksjon.

    Glidelås. Pakk opp!

    Med mindre du har jobbet i Python, er zip/unzip et verktøy du kanskje aldri legger merke til eller forestiller deg i hele din karriere som JavaScript-utvikler. Og kanskje av en god grunn: det er sjelden den typen desperat behov for zip/unzip som det er for filter() osv. Det er imidlertid et av de best mindre kjente verktøyene som finnes og kan hjelpe deg med å lage kortfattet kode i enkelte situasjoner .

    I motsetning til hva det høres ut som, har zip/unzip ingenting med komprimering å gjøre. I stedet er det en grupperingsoperasjon der arrays av samme lengde kan konverteres til en enkelt array av arrays med elementer i samme posisjon pakket sammen (zip()) og tilbake (unzip()). Ja, jeg vet, det blir tåkete å prøve å nøye seg med ord, så la oss se på litt kode:

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

    De originale tre matrisene ble konvertert til en enkelt med kun to matriser. Og hver av disse nye matrisene representerer et enkelt dyr med alt på ett sted. Så, indeksen 0 forteller oss hvilken type dyr det er, indeksen 1 forteller oss størrelsen, og indeksen 2 forteller oss vekten. Som et resultat er dataene nå lettere å jobbe med. Når du har brukt de operasjonene du trenger på dataene, kan du bryte dem opp igjen ved å bruke unzip() og sende dem tilbake til den opprinnelige kilden:

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

    Zip/unzip-verktøyet er ikke noe som vil forandre livet ditt over natten, men det vil forandre livet ditt en dag!

    Konklusjon 👨‍🏫

    (Jeg legger inn all kildekoden som er brukt i denne artikkelen her for deg å prøve direkte fra nettleseren!)

    Lodashen dokumenter er proppfulle av eksempler og funksjoner som bare vil forvirre deg. I en tid hvor masochisme ser ut til å øke i JS-økosystemet, er Lodash som et friskt pust, og jeg anbefaler på det sterkeste at du bruker dette biblioteket i prosjektene dine!