10 важных функций Lodash для разработчиков JavaScript
Опубликовано: 2021-10-05Для разработчиков JavaScript Lodash не нуждается в представлении. Однако библиотека обширна и часто кажется подавляющей. Уже нет!
Лодаш, Лодаш, Лодаш. . . с чего бы мне вообще начать!
Было время, когда только зарождалась экосистема JavaScript; это можно сравнить с диким западом или джунглями, если хотите, где многое происходило, но было очень мало ответов на повседневные разочарования разработчиков и продуктивность.
Затем появился Лодаш, и это было похоже на наводнение, затопившее все. Прямо от простых повседневных нужд, таких как сортировка, до сложных преобразований структур данных, Lodash был загружен (даже перегружен!) Функциями, которые превратили жизнь разработчиков JS в настоящее блаженство.

А где сегодня Лодаш? Что ж, в нем все еще есть все преимущества, которые он предлагал изначально, а затем и некоторые, но, похоже, он потерял свою долю в сообществе JavaScript. Почему? Я могу придумать несколько причин:
- Некоторые функции в библиотеке Lodash были (и остаются) медленными при применении к большим спискам. Хотя это никогда не повлияло бы на 95% проектов, влиятельные разработчики из оставшихся 5% обвинили Lodash в плохой прессе, и эффект перекинулся на широкую публику.
- В экосистеме JS есть тенденция (можно даже сказать то же самое о людях с Голанга), когда высокомерие более распространено, чем необходимо. Итак, полагаться на что-то вроде Lodash - это глупо, и на форумах вроде StackOverflow его отвергают, когда люди предлагают такие решения («Что ?! Использовать целую библиотеку для чего-то вроде этого? Я могу комбинировать
filter()сreduce()для достижения то же самое в простой функции! »). - Лодаш старый. По крайней мере, по стандартам JS. Он вышел в 2012 году, так что на момент написания прошло почти десять лет. API был стабильным, и не так много интересных вещей можно было добавлять каждый год (просто потому, что в этом нет необходимости), что порождает скуку для среднего перевозбужденного JS-разработчика.
На мой взгляд, отказ от Lodash - серьезная потеря для нашей кодовой базы JavaScript. В нем есть проверенные безошибочные и элегантные решения повседневных проблем, с которыми мы сталкиваемся на работе, и его использование только сделает наш код более читабельным и удобным для сопровождения.
С учетом сказанного, давайте погрузимся в некоторые из распространенных (или нет!) Функций Lodash и посмотрим, насколько невероятно полезной и красивой является эта библиотека.
Клонировать. . . глубоко!
Поскольку в JavaScript объекты передаются по ссылке, у разработчиков возникает головная боль, когда они хотят что-то клонировать в надежде, что новый набор данных будет другим.
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' } ] */ Обратите внимание, как при нашей чистой невинности и несмотря на наши благие намерения, исходный массив people мутировал в процессе (специализация Арнольда изменилась с C++ на JS ) - серьезный удар по целостности базовой программной системы! В самом деле, нам нужен способ сделать истинную (глубокую) копию исходного массива.

Возможно, вы возразите, что это «глупый» способ кодирования на JS; однако в действительности все немного сложнее. Да, у нас есть прекрасный оператор деструктурирования, но любой, кто пытался деструктурировать сложные объекты и массивы, знает о боли. Затем есть идея использовать сериализацию и десериализацию (возможно, JSON) для достижения глубокого копирования, но это только усложняет ваш код для читателя.
Напротив, посмотрите, насколько изящно и лаконично получается решение при использовании 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' } ] */ Обратите внимание, как массив people остается нетронутым после глубокого клонирования (в этом случае Арнольд все еще специализируется на C++ ). Но что еще более важно, код прост для понимания.
Удалить дубликаты из массива
Удаление дубликатов из массива звучит как отличная проблема собеседования / интерактивной доски (помните, если сомневаетесь, добавьте хэш-карту в проблему!). И, конечно, вы всегда можете написать для этого специальную функцию, но что, если вы столкнетесь с несколькими различными сценариями, в которых ваши массивы будут уникальными? Вы можете написать для этого несколько других функций (и рискнуть столкнуться с небольшими ошибками) или просто использовать Lodash!

Наш первый пример уникальных массивов довольно тривиален, но он все же отражает скорость и надежность, которые Lodash привносит в таблицу. Представьте себе, что вы делаете это самостоятельно, написав всю настраиваемую логику!
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 ]Обратите внимание, что окончательный массив не сортируется, что, конечно, здесь не имеет значения. Но теперь давайте представим более сложный сценарий: у нас есть массив пользователей, которых мы откуда-то извлекли, но мы хотим убедиться, что он содержит только уникальных пользователей. Легко с 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 } ] */ В этом примере мы использовали метод uniqBy() чтобы сообщить Lodash, что мы хотим, чтобы объекты были уникальными по свойству id . В одной строке мы выразили то, что могло занять 10-20 строк, и представили больше возможностей для ошибок!
Есть много других возможностей сделать вещи уникальными в Lodash, и я рекомендую вам ознакомиться с документацией.
Разница двух массивов
Союз, различие и т. Д. Могут звучать как термины, которые лучше всего оставить позади в скучных лекциях по теории множеств в старших классах, но они чаще встречаются в повседневной практике. Обычно есть список и вы хотите объединить с ним другой список или хотите найти, какие элементы являются уникальными для него по сравнению с другим списком; для этих сценариев функция разности идеальна.

Давайте начнем путешествие по отличиям с простого сценария: вы получили список всех идентификаторов пользователей в системе, а также список тех, чьи учетные записи активны. Как найти неактивные идентификаторы? Все просто, правда?
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 ] А что, если, как это происходит в более реалистичной обстановке, вам придется работать с массивом объектов вместо простых примитивов? Что ж, у Lodash для этого есть хороший метод 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' } ]Аккуратно, правда ?!
Как и разница, в Lodash есть и другие методы для общих операций над множеством: объединение, пересечение и т. Д.
Сглаживающие массивы
Необходимость сглаживания массивов возникает довольно часто. Одним из вариантов использования является то, что вы получили ответ API и вам нужно применить некоторую комбинацию map() и filter() к сложному списку вложенных объектов / массивов, чтобы извлечь, скажем, идентификаторы пользователей, и теперь у вас остается массивы массивов. Вот фрагмент кода, изображающий эту ситуацию:
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); Сможете угадать, как теперь выглядит postPaidUserIds ? Подсказка: это омерзительно!
[ [], [ { userId: 3, date: '2021-08-08', amount: 30, type: 'postpaid' }, { userId: 4, date: '2021-06-06', amount: 330, type: 'postpaid' } ] ] Теперь, если вы разумный человек, вы не хотите писать собственную логику для извлечения объектов порядка и аккуратного размещения их в строке внутри массива. Просто используйте метод flatten() и наслаждайтесь виноградом:
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' } ] */ Обратите внимание, что flatten() идет только на один уровень в глубину. То есть, если ваши объекты застряли на два, три или более уровней глубины, flatten() они вас разочаруют. В таких случаях в Lodash есть метод flattenDeep() , но имейте в flattenDeep() , что применение этого метода к очень большим структурам может замедлить работу (как и за кулисами, работает рекурсивная операция).

Пустой объект / массив?
Благодаря тому, как «ложные» значения и типы работают в JavaScript, иногда такая простая задача, как проверка на пустоту, приводит к экзистенциальному ужасу.

Как проверить, пуст ли массив? Вы можете проверить, length ли его length 0 или нет. Как теперь проверить, пуст ли объект? Ну ... погоди! Здесь возникает это неприятное чувство, и эти примеры JavaScript, содержащие такие вещи, как [] == false и {} == false начинают кружить в наших головах. Когда вы вынуждены реализовать какую-либо функцию, подобные наземные мины - это последнее, что вам нужно - они сделают ваш код трудным для понимания и внесут неопределенность в ваш набор тестов.
Работа с недостающими данными
В реальном мире данные нас слушают; как бы сильно мы этого не хотели, это редко бывает рационально и рационально. Один из типичных примеров - отсутствие нулевых объектов / массивов в большой структуре данных, полученной в качестве ответа API.

Предположим, мы получили в качестве ответа API следующий объект:
const apiResponse = { id: 33467, paymentRefernce: 'AEE3356T68', // `order` object missing processedAt: `2021-10-10 00:00:00`, }; Как показано, мы обычно получаем объект order в ответе от API, но это не всегда так. Итак, что, если у нас есть какой-то код, который полагается на этот объект? Один из способов - это защитный код, но в зависимости от того, насколько вложен объект order , мы скоро будем писать очень уродливый код, если хотим избежать ошибок времени выполнения:
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 ); }Да, очень некрасиво писать, очень некрасиво читать, очень некрасиво поддерживать и так далее. К счастью, у Lodash есть простой способ справиться с такими ситуациями.
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 Также есть фантастический вариант предоставления значения по умолчанию вместо получения 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 Не знаю, как вы, но get() - одна из тех вещей, от которых у меня на глазах появляются слезы счастья. Ничего особенного. Здесь нет синтаксиса или вариантов для запоминания, но посмотрите, сколько коллективных страданий он может облегчить!
Debouncing
Если вы не знакомы, устранение неполадок - обычная тема во фронтенд-разработке. Идея в том, что иногда полезно запускать действие не сразу, а через некоторое время (обычно через несколько миллисекунд). Что это обозначает? Вот пример.

Представьте себе веб-сайт электронной коммерции с панелью поиска (ну, в наши дни любой веб-сайт / веб-приложение!). Для лучшего UX мы не хотим, чтобы пользователю приходилось нажимать клавишу ввода (или, что еще хуже, нажимать кнопку «поиск»), чтобы отображать предложения / превью на основе их поискового запроса. Но очевидный ответ немного загружен: если мы добавим прослушиватель событий в onChange() для панели поиска и onChange() вызов API для каждого нажатия клавиши, мы создали бы кошмар для нашего бэкэнда; было бы слишком много ненужных вызовов (например, если искать «кисть для белого ковра», всего будет 18 запросов!), и почти все они не будут иметь значения, потому что пользовательский ввод еще не завершен.
Ответ заключается в устранении неполадок, а идея заключается в следующем: не отправлять вызов API, как только текст изменится. Подождите некоторое время (скажем, 200 миллисекунд), и если к этому времени произойдет еще одно нажатие клавиши, отмените предыдущий отсчет времени и снова начните ожидание. В результате только тогда, когда пользователь делает паузу (либо потому, что он думает, либо потому, что он готов и ожидает ответа), мы отправляем запрос API на бэкэнд.
Общая стратегия, которую я описал, сложна, и я не буду углубляться в синхронизацию управления таймером и отмены; однако фактический процесс устранения неполадок очень прост, если вы используете 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 Если вы думаете о setTimeout() я бы проделал ту же работу, что ж, это еще не все! Debounce Lodash обладает множеством мощных функций; например, вы можете убедиться, что деблок не бесконечен. То есть, даже если происходит нажатие клавиши каждый раз, когда функция собирается сработать (таким образом, отменяется весь процесс), вы можете убедиться, что вызов API все равно выполняется, скажем, через две секунды. Для этого Lodash debounce() имеет параметр maxWait :
const debouncedFetchDogBreeds = _.debounce(fetchDogBreeds, 150, { maxWait: 2000 }); // debounce for 250ms, but send the API request after 2 seconds anywayОбязательно ознакомьтесь с официальной документацией для более глубокого погружения. Они полны сверхважных вещей!
Удалить значения из массива
Не знаю, как вы, но я ненавижу писать код для удаления элементов из массива. Сначала мне нужно получить индекс элемента, проверить, действительно ли индекс действителен, и если да, вызвать метод splice() и так далее. Я никогда не могу вспомнить синтаксис, и поэтому мне нужно постоянно что-то искать, и в конце у меня остается мучительное чувство, что я позволил закрасться какой-то глупой ошибке.

const greetings = ['hello', 'hi', 'hey', 'wave', 'hi']; _.pull(greetings, 'wave', 'hi'); console.log(greetings); // [ 'hello', 'hey' ]Обратите внимание на две вещи:
- Исходный массив был изменен в процессе.
- Метод
pull()удаляет все экземпляры, даже если есть дубликаты.
Есть еще один связанный метод под названием pullAll() который принимает массив в качестве второго параметра, что упрощает одновременное удаление нескольких элементов. Допустим, мы могли бы просто использовать pull() с оператором распространения, но помните, что Lodash появился в то время, когда оператор распространения даже не был предложением на языке!
const greetings2 = ['hello', 'hi', 'hey', 'wave', 'hi']; _.pullAll(greetings2, ['wave', 'hi']); console.log(greetings2); // [ 'hello', 'hey' ]Последний индекс элемента
Собственный метод indexOf() - это здорово, за исключением случаев, когда вы хотите сканировать массив с противоположного направления! И снова, да, вы могли бы просто написать убывающий цикл и найти элемент, но почему бы не использовать более элегантную технику?

Вот быстрое решение Lodash с использованием метода lastIndexOf() :
const integers = [2, 4, 1, 6, -1, 10, 3, -1, 7]; const index = _.lastIndexOf(integers, -1); console.log(index); // 7К сожалению, нет варианта этого метода, в котором мы могли бы искать сложные объекты или даже передавать пользовательскую функцию поиска.
Почтовый индекс. Распаковать!

Если вы не работали с Python, zip / unzip - это утилита, которую вы, возможно, никогда не заметите или не представите за всю свою карьеру в качестве разработчика JavaScript. И, возможно, по уважительной причине: редко возникает острая необходимость в zip / unzip, как в filter() и т. Д. Однако это одна из лучших малоизвестных утилит в мире и может помочь вам создать сжатый код в некоторых ситуациях. .
Вопреки тому, как это звучит, zip / unzip не имеет ничего общего со сжатием. Вместо этого это операция группирования, при которой массивы одинаковой длины могут быть преобразованы в один массив массивов с элементами в одной и той же позиции, упакованными вместе ( zip() ) и обратно ( unzip() ). Да, я знаю, пытаться обойтись словами становится неясно, так что давайте посмотрим на код:
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 сообщает нам его вес. В результате с данными стало легче работать. После того, как вы применили все необходимые операции к данным, вы можете снова разбить их с помощью unzip() и отправить обратно в исходный источник:
const animalData = _.unzip(groupedAnimals); console.log(animalData); // [ [ 'duck', 'sheep' ], [ 'small', 'large' ], [ 'less', 'more' ] ]Утилита zip / unzip - это не то, что изменит вашу жизнь в одночасье, но однажды она изменит вашу жизнь!
Вывод
(Я поместил сюда весь исходный код, использованный в этой статье, чтобы вы могли попробовать прямо из браузера!)
Документация Lodash полна примеров и функций, которые просто поразят вас. В наши дни, когда мазохизм в экосистеме JS, кажется, усиливается, Lodash похож на глоток свежего воздуха, и я настоятельно рекомендую вам использовать эту библиотеку в своих проектах!
