10 важних Лодасх функција за ЈаваСцрипт програмере

За ЈаваСцрипт програмере, Лодасх не треба представљати. Међутим, библиотека је огромна и често се осећа огромном. Не више!

Лодаш, Лодаш, Лодаш. . . одакле да почнем! 🤔

Било је времена када је ЈаваСцрипт екосистем настајао; могао би се упоредити са дивљим западом или џунглом, ако хоћете, где се много тога дешавало, али је било врло мало одговора за свакодневне фрустрације програмера и продуктивност.

Онда Лодасх ступио на сцену, и осећао се као поплава која је све потопила. Од једноставних свакодневних потреба као што је сортирање до сложених трансформација структуре података, Лодасх је дошао препун (преоптерећен, чак!) са функционалношћу која је живот ЈС програмера претворила у чисто блаженство.

Здраво, Лодасх!

А где је данас Лодаш? Па, још увек има све доброте које је нудио у почетку, а затим и неке, али изгледа да је изгубио удео у ЈаваСцрипт заједници. Зашто? Могу да се сетим неколико разлога:

  • Неке функције у Лодасх библиотеци биле су (и још увек су) споре када се примењују на велике листе. Иако ово никада не би утицало на 95% пројеката тамо, утицајни програмери из преосталих 5% дали су лошу штампу Лодасх-у и ефекат се спустио на основну.
  • Постоји тренд у ЈС екосистему (могао би чак рећи исто о људима из Голанга) где је охолост чешћа него што је потребно. Дакле, ослањање на нешто као што је Лодасх сматра се глупим и обара се на форумима као што је СтацкОверфлов када људи предлажу таква решења („Шта?! Користити целу библиотеку за нешто попут овога? Могу комбиновати филтер() са редукцијом() да бих постигао иста ствар у једноставној функцији!“).
  • Лодаш је стар. Барем по ЈС стандардима. Изашао је 2012. године, тако да је од писања прошло скоро десет година. АПИ је стабилан и не може се додати много узбудљивих ствари сваке године (једноставно зато што нема потребе), што ствара досаду за просечног преузбуђеног ЈС програмера.

По мом мишљењу, некоришћење Лодасх-а представља значајан губитак за наше ЈаваСцрипт базе кода. Има доказано елегантна решења без грешака за свакодневне проблеме на које наилазимо на послу, а његово коришћење ће само учинити наш код читљивијим и лакшим за одржавање.

Уз то, хајде да заронимо у неке од уобичајених (или не!) Лодасх функција и видимо колико је ова библиотека невероватно корисна и лепа.

Клон . . . дубоко!

Пошто се објекти прослеђују по референци у ЈаваСцрипт-у, то ствара главобољу за програмере када желе да клонирају нешто у нади да је нови скуп података другачији.

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

Запазите како је у нашој чистој невиности и упркос нашим добрим намерама, оригинални низ људи мутирао у том процесу (Арнолдова специјализација је промењена са Ц++ на ЈС) — велики ударац интегритету основног софтверског система! Заиста, потребан нам је начин да направимо праву (дубоку) копију оригиналног низа.

Здраво Дејв, упознај Дејва!

Можда можете тврдити да је ово „блесав“ начин кодирања у ЈС-у; међутим, стварност је мало компликована. Да, имамо на располагању диван оператор деструктурирања, али свако ко је покушао да деструктурира сложене објекте и низове зна бол. Затим, постоји идеја да се користи серијализација и де-сериализација (можда ЈСОН) за постизање дубоког копирања, али то само чини ваш код неуреднијим за читаоца.

Насупрот томе, погледајте како је невероватно елегантно и концизно решење када се Лодасх навикне:

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

Приметите како је низ људи нетакнут након дубоког клонирања (Арнолд је још увек специјализован за Ц++ у овом случају). Али што је још важније, код је једноставан за разумевање.

  ЈБудс Мини су направљени за људе са малим ушима

Уклоните дупликате из низа

Уклањање дупликата из низа звучи као одличан проблем интервјуа/беле табле (запамтите, када сте у недоумици, баците хасхмап на проблем!). И, наравно, увек можете да напишете прилагођену функцију да бисте то урадили, али шта ако наиђете на неколико различитих сценарија у којима ћете своје низове учинити јединственим? Можете написати неколико других функција за то (и ризиковати да наиђете на суптилне грешке), или можете једноставно користити Лодасх!

Наш први пример јединствених низова је прилично тривијалан, али и даље представља брзину и поузданост које Лодасх доноси на сто. Замислите да ово радите тако што ћете сами написати сву прилагођену логику!

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 ]

Приметите да коначни низ није сортиран, што, наравно, овде није од значаја. Али сада, замислимо компликованији сценарио: имамо низ корисника које смо однекуд извукли, али желимо да будемо сигурни да садржи само јединствене кориснике. Лако са Лодасх-ом!

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

У овом примеру, користили смо метод уникБи() да кажемо Лодасх-у да желимо да објекти буду јединствени на својству ид. У једном реду смо изразили оно што је могло заузети 10-20 редова и увели више простора за грешке!

Постоји много више доступних ствари око стварања јединствених ствари у Лодасх-у, и препоручујем вам да погледате доцс.

Разлика два низа

Унија, разлика, итд., могу звучати као термини које је најбоље оставити у досадним средњошколским предавањима о Теорији скупова, али се чешће појављују у свакодневној пракси. Уобичајено је да имате листу и желите да спојите другу листу са њом или желите да пронађете који елементи су јединствени за њу у поређењу са другом листом; за ове сценарије, функција разлике је савршена.

Здраво, А. Збогом, Б!

Хајде да започнемо путовање разлике уз помоћ једноставног сценарија: добили сте листу свих ИД-ова корисника у систему, као и листу оних чији су налози активни. Како проналазите неактивне ИД-ове? Једноставно, зар не?

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 ]

А шта ако, као што се дешава у реалистичнијем окружењу, морате да радите са низом објеката уместо с обичним примитивима? Па, Лодасх има лепу разликуБи() методу за ово!

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

Уредно, зар не?!

Као разлика, постоје и друге методе у Лодасх-у за заједничке операције скупова: унија, пресек, итд.

Равни низови

Потреба за изравнавањем низова јавља се прилично често. Један случај употребе је да сте добили АПИ одговор и да морате да примените неку комбинацију мап() и филтер() на сложену листу угнежђених објеката/низова да бисте извукли, рецимо, корисничке ИД-ове, и сада вам остаје низови низова. Ево исечка кода који приказује ову ситуацију:

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

Можете ли да погодите како постПаидУсерИдс сада изгледа? Наговештај: одвратно је!

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

Сада, ако сте разумна особа, не желите да пишете прилагођену логику да бисте издвојили објекте редоследа и лепо их поставили у ред унутар низа. Само користите метод флаттен() и уживајте у грожђу:

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

Имајте на уму да флаттен() иде само један ниво дубоко. То је ако су ваши објекти заглављени два, три или више нивоа дубоко, флаттен() они ће вас разочарати. У тим случајевима, Лодасх има метод флаттенДееп(), али имајте на уму да примена ове методе на веома великим структурама може успорити ствари (као иза сцене, постоји рекурзивна операција).

  Како додати, прилагодити и користити виџете на Мац-у

Да ли је објекат/низ празан?

Захваљујући томе како „лажне“ вредности и типови функционишу у ЈаваСцрипт-у, понекад нешто тако једноставно као што је провера празнине резултира егзистенцијалним страхом.

Како проверити да ли је низ празан? Можете проверити да ли је његова дужина 0 или не. Сада, како да проверите да ли је објекат празан? Па… чекај мало! Овде се појављује тај нелагодан осећај и они ЈаваСцрипт примери који садрже ствари попут [] == лажно и {} == лажно почиње да кружи нашим главама. Када сте под притиском да испоручите неку функцију, овакве нагазне мине су последња ствар која вам је потребна — оне ће отежати разумевање вашег кода и унеће несигурност у ваш тест пакет.

Рад са подацима који недостају

У стварном свету, подаци нас слушају; без обзира колико то јако желимо, ретко је рационално и разумно. Један типичан пример је недостатак нултих објеката/низова у великој структури података примљеној као АПИ одговор.

Претпоставимо да смо добили следећи објекат као АПИ одговор:

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

Као што је приказано, генерално добијамо објекат налога у одговору од АПИ-ја, али то није увек случај. Дакле, шта ако имамо неки код који се ослања на овај објекат? Један од начина би био да кодирамо дефанзивно, али у зависности од тога колико је угнежђен објекат наруџбе, ускоро бисмо писали веома ружан код ако желимо да избегнемо грешке током извршавања:

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

🤢🤢 Да, веома ружно за писање, веома ружно за читање, веома ружно за одржавање и тако даље. Срећом, Лодасх има једноставан начин да се носи са таквим ситуацијама.

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

Ту је и фантастична опција да се обезбеди подразумевана вредност уместо да буде недефинисана за ствари које недостају:

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

Не знам за вас, али гет() је једна од оних ствари које ми тјерају сузе среће на очи. Није ништа блиставо. Не постоји консултована синтакса или опције за памћење, али погледајте колику колективну патњу може да ублажи! 😇

Дебоунцинг

У случају да нисте упознати, одбијање је уобичајена тема у развоју фронтенда. Идеја је да је понекад корисно покренути акцију не одмах, већ након неког времена (обично неколико милисекунди). Шта то значи? Ево примера.

Замислите веб локацију за е-трговину са траком за претрагу (па, било која веб локација/веб апликација ових дана!). За бољи УКС, не желимо да корисник мора да притисне ентер (или још горе, притисне дугме „претражи“) да би приказао предлоге/прегледе на основу свог термина за претрагу. Али очигледан одговор је мало оптерећен: ако бисмо додали слушалац догађаја у онЦханге() за траку за претрагу и покренули АПИ позив за сваки притисак на тастер, направили бисмо ноћну мору за наш позадински систем; било би превише непотребних позива (на пример, ако се тражи „четка за беле тепихе“, биће укупно 18 захтева!) и скоро сви ће бити небитни јер кориснички унос није завршен.

Одговор лежи у одбијању, а идеја је следећа: немојте слати АПИ позив чим се текст промени. Сачекајте неко време (рецимо, 200 милисекунди) и ако до тог тренутка дође до још једног притиска на тастер, откажите раније одбројавање времена и поново почните да чекате. Као резултат тога, тек када корисник паузира (било зато што размишља или зато што је завршио и очекује неки одговор), шаљемо АПИ захтев бацкенду.

Целокупна стратегија коју сам описао је компликована и нећу се упуштати у синхронизацију управљања тајмером и отказивања; међутим, стварни процес уклањања одскока је веома једноставан ако користите Лодасх.

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

Ако мислите на сетТимеоут() ја бих урадио исти посао, па, има још тога! Лодасх-ов дебоунце долази са многим моћним карактеристикама; на пример, можда бисте желели да осигурате да одбијање није неограничено. Односно, чак и ако постоји притисак на тастер сваки пут када се функција спрема да се покрене (чиме се поништава целокупни процес), можда бисте желели да се уверите да је АПИ позив ипак обављен после, рецимо, две секунде. За ово, Лодасх дебоунце() има опцију макВаит:

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

Проверите званичника доцс за дубље роњење. Они су пуни супер важних ствари!

  Како претраживати по корисничком имену на ОкЦупид-у

Уклоните вредности из низа

Не знам за вас, али ја мрзим да пишем код за уклањање ставки из низа. Прво, морам да добијем индекс ставке, да проверим да ли је индекс заиста валидан и ако јесте, позвати метод сплице() и тако даље. Никада не могу да се сетим синтаксе и зато морам стално да тражим ствари, а на крају остајем са мучним осећајем да сам пустио неку глупу грешку да се увуче.

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

Имајте на уму две ствари:

  • Оригинални низ је промењен током процеса.
  • Метода пулл() уклања све инстанце, чак и ако постоје дупликати.
  • Постоји још једна сродна метода која се зове пуллАлл() која прихвата низ као други параметар, што олакшава уклањање више ставки одједном. Подразумева се да бисмо могли само да користимо пулл() са оператором ширења, али запамтите да је Лодасх дошао у време када оператор ширења није био чак ни предлог у језику!

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

    Последњи индекс елемента

    ЈавсСцрипт-ов изворни индекОф() метод је кул, осим када сте заинтересовани да скенирате низ из супротног смера! И опет, да, могли бисте једноставно написати декрементирајућу петљу и пронаћи елемент, али зашто не бисте користили много елегантнију технику?

    Ево брзог решења за Лодасх користећи метод ластИндекОф():

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

    Нажалост, не постоји варијанта ове методе у којој можемо тражити сложене објекте или чак проследити прилагођену функцију тражења.

    Зип. Распакујте!

    Осим ако нисте радили у Питхон-у, зип/унзип је услужни програм који можда никада нећете приметити или замислити у целој својој каријери ЈаваСцрипт програмера. И можда из доброг разлога: ретко постоји врста очајничке потребе за зип/унзип-ом као што постоји за филтер(), итд. Међутим, то је један од најбољих мање познатих услужних програма и може вам помоћи да направите сажет код у неким ситуацијама .

    Супротно ономе што звучи, зип/унзип нема никакве везе са компресијом. Уместо тога, то је операција груписања где се низови исте дужине могу конвертовати у један низ низова са елементима на истој позицији спакованим заједно (зип()) и назад (унзип()). Да, знам, постаје мутно покушавати да се задовољим речима, па хајде да погледамо неки код:

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

    Оригинална три низа су конвертована у један са само два низа. И сваки од ових нових низова представља једну животињу са свим садржајима на једном месту. Дакле, индекс 0 нам говори о којој врсти животиње се ради, индекс 1 нам говори о њеној величини, а индекс 2 о њеној тежини. Као резултат тога, сада је лакше радити са подацима. Када примените све операције које су вам потребне на податке, можете их поново разбити користећи унзип() и послати их назад оригиналном извору:

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

    Услужни програм зип/унзип није нешто што ће вам променити живот преко ноћи, али ће једног дана променити ваш живот!

    Закључак 👨‍🏫

    (Ставио сам сав изворни код који се користи у овом чланку овде да бисте покушали директно из претраживача!)

    Тхе Лодасх доцс су препуни примера и функција које ће вас једноставно одушевити. У дану и времену када се чини да мазохизам расте у ЈС екосистему, Лодасх је као дах свежег ваздуха, и топло препоручујем да користите ову библиотеку у својим пројектима!