10 ważnych funkcji Lodash dla programistów JavaScript
Opublikowany: 2021-10-05Programistom JavaScript nie trzeba przedstawiać Lodash. Jednak biblioteka jest ogromna i często wydaje się przytłaczająca. Nigdy więcej!
Lodasz, Lodasz, Lodasz. . . od czego mam zacząć!
Był czas, kiedy powstawał ekosystem JavaScript; można go porównać do dzikiego zachodu lub dżungli, jeśli wolisz, gdzie dużo się działo, ale było bardzo niewiele odpowiedzi na codzienne frustracje programistów i produktywność.
Wtedy Lodash wszedł na scenę i poczuł się jak powódź, która zatopiła wszystko. Od prostych codziennych potrzeb, takich jak sortowanie, po skomplikowane przekształcenia struktury danych, Lodash został przeładowany (a nawet przeładowany!) funkcjami, które zmieniły życie programistów JS w czystą błogość.

A gdzie jest dzisiaj Lodash? No cóż, nadal ma wszystkie zalety, które początkowo oferował, a potem trochę, ale wydaje się, że stracił udział w społeczności JavaScript. Czemu? Przychodzi mi do głowy kilka powodów:
- Niektóre funkcje w bibliotece Lodash były (i nadal są) powolne, gdy są stosowane do dużych list. Chociaż nigdy nie wpłynęłoby to na 95% projektów, wpływowi deweloperzy z pozostałych 5% dali Lodashowi złą prasę, a efekt spłynął na oddolne osoby.
- Istnieje tendencja w ekosystemie JS (może nawet powiedzieć to samo o ludziach z Golang), gdzie arogancja jest bardziej powszechna niż to konieczne. Tak więc poleganie na czymś takim jak Lodash jest postrzegane jako głupie i zostaje zestrzelone na forach takich jak StackOverflow, gdy ludzie sugerują takie rozwiązania („Co?! Użyj całej biblioteki do czegoś takiego? Mogę połączyć
filter()zreduce()aby osiągnąć to samo w prostej funkcji!”). - Lodash jest stary. Przynajmniej według standardów JS. Wyszła w 2012 roku, więc w chwili pisania tego tekstu minęło już prawie dziesięć lat. Interfejs API jest stabilny i co roku nie można dodawać zbyt wielu ekscytujących rzeczy (po prostu dlatego, że nie ma takiej potrzeby), co generuje nudę dla przeciętnego, nadmiernie podekscytowanego programisty JS.
Moim zdaniem nieużywanie Lodash to znacząca strata dla naszych baz kodu JavaScript. Jest sprawdzonym bezbłędnym i eleganckim rozwiązaniem codziennych problemów, które napotykamy w pracy, a korzystanie z niego tylko sprawi, że nasz kod będzie bardziej czytelny i łatwiejszy w utrzymaniu.
Powiedziawszy to, zagłębmy się w niektóre z popularnych (lub nie!) funkcji Lodash i zobaczmy, jak niesamowicie pomocna i piękna jest ta biblioteka.
Klonuj . . . głęboko!
Ponieważ obiekty są przekazywane przez referencje w JavaScript, powoduje to ból głowy dla programistów, którzy chcą sklonować coś z nadzieją, że nowy zestaw danych będzie inny.
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' } ] */ Zwróć uwagę, jak w naszej czystej niewinności i pomimo naszych dobrych intencji, pierwotny szereg people uległ w tym procesie mutacji (specjalizacja Arnolda zmieniła się z C++ na JS ) — poważny cios w integralność podstawowego systemu oprogramowania! Rzeczywiście, potrzebujemy sposobu na wykonanie prawdziwej (głębokiej) kopii oryginalnej tablicy.

Być może można argumentować, że jest to „głupi” sposób kodowania w JS; jednak rzeczywistość jest nieco skomplikowana. Tak, mamy wspaniały operator destrukturyzujący, ale każdy, kto próbował destrukturyzować złożone obiekty i tablice, zna ten problem. Następnie pojawia się pomysł użycia serializacji i deserializacji (być może JSON), aby uzyskać głębokie kopiowanie, ale to tylko sprawia, że kod staje się bardziej nieuporządkowany dla czytelnika.
Z kolei spójrz, jak niesamowicie eleganckie i zwięzłe jest rozwiązanie, gdy używa się 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' } ] */ Zwróć uwagę, że tablica people pozostaje nietknięta po głębokim klonowaniu (w tym przypadku Arnold nadal specjalizuje się w C++ ). Ale co ważniejsze, kod jest łatwy do zrozumienia.
Usuń duplikaty z tablicy
Usuwanie duplikatów z tablicy brzmi jak doskonały problem z wywiadem/tablicą (pamiętaj, jeśli masz wątpliwości, rzuć hashmapą na problem!). I oczywiście zawsze możesz napisać niestandardową funkcję, aby to zrobić, ale co, jeśli napotkasz kilka różnych scenariuszy, w których Twoje tablice będą unikalne? Możesz napisać do tego kilka innych funkcji (i zaryzykować napotkanie subtelnych błędów) lub po prostu użyć Lodash!

Nasz pierwszy przykład unikalnych tablic jest dość trywialny, ale nadal reprezentuje szybkość i niezawodność, które Lodash wnosi do stołu. Wyobraź sobie, że robisz to samodzielnie, pisząc całą niestandardową logikę!
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 ]Zauważ, że ostateczna tablica nie jest posortowana, co oczywiście nie ma tutaj żadnego znaczenia. Ale teraz wyobraźmy sobie bardziej skomplikowany scenariusz: mamy zbiór użytkowników, których gdzieś pobraliśmy, ale chcemy mieć pewność, że zawiera tylko unikalnych użytkowników. Łatwo z Lodashem!
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 } ] */ W tym przykładzie użyliśmy metody uniqBy() , aby poinformować Lodash, że chcemy, aby obiekty we właściwości id były unikatowe. W jednym wierszu opisaliśmy, co mogło zająć 10-20 wierszy i wprowadziliśmy większy zakres błędów!
Dostępnych jest o wiele więcej rzeczy dotyczących tworzenia wyjątkowych rzeczy w Lodash i zachęcam do zapoznania się z dokumentacją.
Różnica dwóch tablic
Unia, różnica itp. mogą brzmieć jak terminy, które najlepiej pozostawić na nudnych wykładach w szkole średniej na temat teorii mnogości, ale pojawiają się one częściej niż nie w codziennej praktyce. Często zdarza się, że masz listę i chcesz połączyć z nią inną listę lub chcesz znaleźć elementy, które są dla niej unikalne w porównaniu z inną listą; w tych scenariuszach funkcja różnicy jest idealna.

Zacznijmy podróż różnic od prostego scenariusza: otrzymałeś listę wszystkich identyfikatorów użytkowników w systemie, a także listę tych, których konta są aktywne. Jak znaleźć nieaktywne identyfikatory? Proste, prawda?
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 ] A co jeśli, jak to się dzieje w bardziej realistycznym otoczeniu, będziesz musiał pracować z szeregiem obiektów zamiast zwykłych prymitywów? Cóż, Lodash ma do tego fajną metodę różnicyBy differenceBy() !
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' } ]Fajnie, prawda?!
Podobnie jak różnica, w Lodash istnieją inne metody dla typowych operacji na zbiorach: suma, przecięcie itp.
Spłaszczanie tablic
Potrzeba spłaszczania tablic pojawia się dość często. Jednym z przypadków użycia jest to, że otrzymałeś odpowiedź API i musisz zastosować kombinację map() i filter() na złożonej liście zagnieżdżonych obiektów/tablic, aby wyłuskać, powiedzmy, identyfikatory użytkowników, a teraz pozostajesz z tablice tablic. Oto fragment kodu przedstawiający tę sytuację:
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); Czy wiesz, jak teraz wygląda postPaidUserIds ? Podpowiedź: to obrzydliwe!
[ [], [ { userId: 3, date: '2021-08-08', amount: 30, type: 'postpaid' }, { userId: 4, date: '2021-06-06', amount: 330, type: 'postpaid' } ] ] Teraz, jeśli jesteś rozsądną osobą, nie chcesz pisać niestandardowej logiki, aby wyodrębnić obiekty porządku i ułożyć je ładnie w rzędzie wewnątrz tablicy. Po prostu użyj metody flatten() i ciesz się winogronami:
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' } ] */ Zwróć uwagę, że flatten() idzie tylko o jeden poziom w głąb. Oznacza to, że jeśli twoje obiekty utkną na dwóch, trzech lub więcej poziomach głęboko, flatten() rozczarują cię. W takich przypadkach Lodash ma flattenDeep() , ale należy pamiętać, że zastosowanie tej metody do bardzo dużych struktur może spowolnić działanie (ponieważ za kulisami działa operacja rekurencyjna).

Czy obiekt/tablica jest pusta?
Dzięki temu, jak „fałszywe” wartości i typy działają w JavaScript, czasami coś tak prostego, jak sprawdzanie, czy nie ma pustki, wywołuje egzystencjalny strach.

Jak sprawdzić, czy tablica jest pusta? Możesz sprawdzić, czy jego length wynosi 0 czy nie. Jak teraz sprawdzić, czy obiekt jest pusty? Cóż… poczekaj chwilę! W tym momencie pojawia się to nieprzyjemne uczucie, a te przykłady JavaScript zawierające takie rzeczy jak [] == false i {} == false zaczynają krążyć po naszych głowach. Kiedy jest pod presją dostarczenia funkcji, miny takie jak te są ostatnią rzeczą, jakiej potrzebujesz — sprawią, że Twój kod będzie trudny do zrozumienia i wprowadzą niepewność w twoim zestawie testowym.
Praca z brakującymi danymi
W prawdziwym świecie dane nas słuchają; bez względu na to, jak bardzo tego chcemy, rzadko jest to usprawnione i rozsądne. Jednym z typowych przykładów jest brak obiektów/tablic zerowych w dużej strukturze danych otrzymanych jako odpowiedź API.

Załóżmy, że otrzymaliśmy następujący obiekt jako odpowiedź API:
const apiResponse = { id: 33467, paymentRefernce: 'AEE3356T68', // `order` object missing processedAt: `2021-10-10 00:00:00`, }; Jak pokazano, zazwyczaj otrzymujemy obiekt order w odpowiedzi z interfejsu API, ale nie zawsze tak jest. A co, jeśli mamy jakiś kod, który opiera się na tym obiekcie? Jednym ze sposobów byłoby kodowanie defensywne, ale w zależności od tego, jak zagnieżdżony jest obiekt order , wkrótce będziemy pisać bardzo brzydki kod, jeśli chcemy uniknąć błędów w czasie wykonywania:
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 ); }Tak, bardzo brzydkie w pisaniu, bardzo brzydkie w czytaniu, bardzo brzydkie w utrzymaniu i tak dalej. Na szczęście Lodash ma prosty sposób radzenia sobie z takimi sytuacjami.
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 Istnieje również fantastyczna opcja podania wartości domyślnej zamiast undefined brakujących rzeczy:
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 Nie wiem jak wy, ale get() to jedna z tych rzeczy, które wywołują łzy szczęścia w moich oczach. To nie jest nic krzykliwego. Nie ma konsultowanej składni ani opcji do zapamiętania, ale spójrz na ilość zbiorowego cierpienia, które może złagodzić!
Odbijanie
Jeśli nie jesteś zaznajomiony, debouncing jest częstym tematem w tworzeniu frontendu. Chodzi o to, że czasami dobrze jest uruchomić akcję nie od razu, ale po pewnym czasie (zwykle kilka milisekund). Co to znaczy? Oto przykład.

Wyobraź sobie witrynę e-commerce z paskiem wyszukiwania (w dzisiejszych czasach dowolna witryna/aplikacja internetowa!). Aby uzyskać lepszy UX, nie chcemy, aby użytkownik musiał naciskać Enter (lub, co gorsza, przycisk „szukaj”), aby wyświetlić sugestie/podglądy na podstawie wyszukiwanego hasła. Ale oczywista odpowiedź jest trochę obciążona: jeśli dodamy detektor zdarzeń do onChange() dla paska wyszukiwania i uruchomimy wywołanie API dla każdego naciśnięcia klawisza, stworzylibyśmy koszmar dla naszego backendu; byłoby zbyt wiele niepotrzebnych wywołań (na przykład, jeśli wyszukiwane jest „pędzel do białego dywanu”, będzie łącznie 18 żądań!) i prawie wszystkie z nich będą nieistotne, ponieważ dane wejściowe użytkownika nie zostały zakończone.
Odpowiedź leży w debouncingu, a idea jest taka: nie wysyłaj wywołania API, gdy tylko zmieni się tekst. Odczekaj trochę czasu (powiedzmy 200 milisekund) i jeśli do tego czasu nastąpi kolejne naciśnięcie klawisza, anuluj wcześniejsze odliczanie czasu i ponownie zacznij czekać. W rezultacie tylko wtedy, gdy użytkownik się zatrzyma (ponieważ myśli lub skończył i oczekuje jakiejś odpowiedzi), wysyłamy żądanie API do zaplecza.
Ogólna strategia, którą opisałem, jest skomplikowana i nie będę zagłębiać się w synchronizację zarządzania i anulowania timera; jednak rzeczywisty proces usuwania odbić jest bardzo prosty, jeśli używasz 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 Jeśli myślisz, że setTimeout() wykonałbym tę samą pracę, cóż, jest więcej! Odbicie Lodash zawiera wiele potężnych funkcji; na przykład możesz chcieć upewnić się, że odrzucenie nie jest nieokreślone. Oznacza to, że nawet jeśli następuje naciśnięcie klawisza za każdym razem, gdy funkcja ma zostać uruchomiona (a tym samym anuluje cały proces), możesz chcieć upewnić się, że wywołanie API zostanie wykonane mimo to po, powiedzmy, dwóch sekundach. W tym celu Lodash debounce debounce() ma opcję maxWait :
const debouncedFetchDogBreeds = _.debounce(fetchDogBreeds, 150, { maxWait: 2000 }); // debounce for 250ms, but send the API request after 2 seconds anywaySprawdź oficjalne dokumenty, aby uzyskać głębsze nurkowanie. Są pełne super ważnych rzeczy!
Usuń wartości z tablicy
Nie wiem jak wy, ale nienawidzę pisać kodu do usuwania elementów z tablicy. Najpierw muszę pobrać indeks elementu, sprawdzić, czy indeks jest rzeczywiście poprawny, a jeśli tak, wywołać metodę splice() i tak dalej. Nigdy nie pamiętam składni i dlatego muszę cały czas sprawdzać, a pod koniec mam dokuczliwe uczucie, że pozwoliłem, aby wkradł się jakiś głupi błąd.

const greetings = ['hello', 'hi', 'hey', 'wave', 'hi']; _.pull(greetings, 'wave', 'hi'); console.log(greetings); // [ 'hello', 'hey' ]Proszę zwrócić uwagę na dwie rzeczy:
- Oryginalna tablica została w tym czasie zmieniona.
- Metoda
pull()usuwa wszystkie instancje, nawet jeśli są duplikaty.
Istnieje inna powiązana metoda o nazwie pullAll() która akceptuje tablicę jako drugi parametr, co ułatwia usuwanie wielu elementów naraz. To prawda, że możemy po prostu użyć pull() z operatorem rozsunięcia, ale pamiętaj, że Lodash pojawił się w czasie, gdy operator rozsunięcia nie był nawet propozycją w języku!
const greetings2 = ['hello', 'hi', 'hey', 'wave', 'hi']; _.pullAll(greetings2, ['wave', 'hi']); console.log(greetings2); // [ 'hello', 'hey' ]Ostatni indeks elementu
Natywna metoda indexOf() JavsScript jest fajna, z wyjątkiem sytuacji, gdy interesuje Cię skanowanie tablicy z przeciwnego kierunku! I znowu, tak, możesz po prostu napisać dekrementującą pętlę i znaleźć element, ale dlaczego nie użyć o wiele bardziej eleganckiej techniki?

Oto szybkie rozwiązanie Lodash przy użyciu metody lastIndexOf() :
const integers = [2, 4, 1, 6, -1, 10, 3, -1, 7]; const index = _.lastIndexOf(integers, -1); console.log(index); // 7Niestety nie ma wariantu tej metody, w którym możemy wyszukiwać złożone obiekty, a nawet przekazywać niestandardową funkcję wyszukiwania.
Zamek błyskawiczny. Rozsunąć suwak!

Jeśli nie pracowałeś w Pythonie, zip/unzip to narzędzie, którego możesz nigdy nie zauważyć lub wyobrazić sobie w całej swojej karierze programisty JavaScript. I być może z dobrego powodu: rzadko istnieje desperacka potrzeba zip/unzip, jak w przypadku filter() itp. Jest to jednak jedno z najlepszych mniej znanych narzędzi i może pomóc w tworzeniu zwięzłego kodu w niektórych sytuacjach .
Wbrew pozorom zip/unzip nie ma nic wspólnego z kompresją. Zamiast tego jest to operacja grupowania, w której tablice o tej samej długości można przekształcić w pojedynczą tablicę tablic z elementami w tej samej pozycji spakowanymi razem ( zip() ) iz powrotem ( unzip() ). Tak, wiem, robi się zamglony, próbując poradzić sobie ze słowami, więc spójrzmy na kod:
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' ] ] Pierwotne trzy tablice zostały przekształcone w jedną z tylko dwiema tablicami. A każda z tych nowych tablic reprezentuje jedno zwierzę z całym jego miejscem w jednym miejscu. Tak więc indeks 0 mówi nam, jakiego rodzaju jest zwierzę, indeks 1 mówi nam o jego wielkości, a indeks 2 mówi nam o jego wadze. Dzięki temu praca z danymi jest teraz łatwiejsza. Po zastosowaniu wszystkich wymaganych operacji na danych, możesz je ponownie rozbić za pomocą funkcji unzip() i odesłać z powrotem do oryginalnego źródła:
const animalData = _.unzip(groupedAnimals); console.log(animalData); // [ [ 'duck', 'sheep' ], [ 'small', 'large' ], [ 'less', 'more' ] ]Narzędzie zip/unzip nie zmieni twojego życia z dnia na dzień, ale pewnego dnia zmieni twoje życie!
Wniosek
(Umieściłem tutaj cały kod źródłowy użyty w tym artykule, abyś mógł wypróbować go bezpośrednio z przeglądarki!)
Dokumentacja Lodash jest pełna przykładów i funkcji, które po prostu rozwalą twój umysł. W czasach, gdy masochizm wydaje się narastać w ekosystemie JS, Lodash jest jak powiew świeżego powietrza i gorąco polecam korzystanie z tej biblioteki w swoich projektach!
