Задачи по JavaScript для подготовки к собеседованию. Часть 2
В этой статье я рассмотрю 5 задач, проверяющих знание JavaScript. Каждая их них снабжена объяснением и алгоритмом решения. В конце я даю ссылки на документацию. Это вторая часть серии статей, и она посвящена самостоятельной реализации встроенных методов JavaScript-массивов forEach, map, filter, some, even и reduce. Первую часть вы можете прочитать по ссылке.
Перед тем как начать, хочу отметить, что все 5 задач однотипны. Успешность их решения отражает понимание того, как на самом деле устроены рассматриваемые нативные методы.
Метод forEach
Этот метод применяется для простого перебора массива и совершения действий с его элементами. Существует мнение, что его вообще не стоит касаться и что лучше просто обойтись вариациями цикла for. Как бы то ни было, этот метод используется повсеместно. На собеседовании могут спросить, как он устроен, и попросить написать собственную реализацию.
Обязательные условия
Прежде чем начать, стоит разобраться, что должна принимать в качестве параметров наша функция:
- arr – массив, который нужно перебрать;
- callbackFn – функция обратного вызова, обрабатывающая каждый элемент перебираемого массива:
- element – элемент массива, возвращаемый в callback-функцию;
- index – индекс этого элемента;
- array – исходный массив, который мы перебираем.
- thisArg – передаваемый в функцию контекст.
Основная идея
Весь смысл реализации заключается в том, чтобы просто перебрать элементы массива один за другим. При этом не стоит забывать, что должна быть возможность передать в функцию контекст this. Он может понадобиться внутри функции обратного вызова, которая, в свою очередь, возвращает элемент массива, его индекс и его самого.
Решение
1. Создаем функцию forEach, указав в качестве параметров:
- arr – массив;
- callback – функцию обратного вызова;
- thisArg – контекст this, это необязательный параметр.
2. Внутри тела функции перебираем массив arr при помощи цикла for;
3. Внутри цикла вызываем callback-функцию при помощи метода .call(). Это необходимо для передачи в неё контекста thisArg. Итак, передаем в него следующие параметры:
- thisArg – переданный извне контекст this;
- arr[index] – текущий элемент массива;
- index – индекс элемента массива;
- arr – исходный массив.
Метод map
Метод массива map используется, чтобы перебрать элементы и провести их модификацию. Например, массив состоит из объектов и к каждому из них нужно добавить новое свойство или удалить у него какие-нибудь данные.
Обязательные условия
Функция должна принимать в себя следующие параметры:
- arr – массив, который нужно перебрать для модификации его элементов;
- callbackFn – функция, обрабатывающая каждый элемент перебираемого массива, она должна возвращать измененные или новые данные на место этого элемента:
- element – элемент массива возвращаемый в callback-функцию;
- index – индекс этого элемента.;
- array – исходный массив.
- thisArg – передаваемый в функцию контекст this, это необязательный параметр.
Основная идея
Как и в реализации метода forEach, мы можем перебирать элементы массива, но при этом еще и возвращать модифицированные данные. Здесь также важно не забыть реализовать возможность передачи контекста this. Основное назначение этой функции в том, чтобы модифицировать коллекции данных. Например, мы можем получить массив данных пользователей с сервера, но по какой-то причине у них будут отсутствовать уникальные идентификаторы. Но они могут понадобиться для вывода данных в таблице в браузере – или для сортировки, сокрытия каких-то элементов и т.д. При помощи метода map мы можем это исправить, перебрав массив и добавив в каждый элемент по уникальному идентификатору.
Решение
- Создаем функцию map, указав в качестве параметров:
- arr – массив;
- callback – функция обратного вызова;
- thisArg – контекст this, это необязательный параметр.
- Внутри функции создаем контейнер, в который будем помещать модифицированные данные из массива;
- При помощи цикла for перебираем массив, где вызываем callback-функцию при помощи метода .call(), это нужно для того, чтобы передать в неё контекст this;
- В функцию обратного вызова передаем в параметры:
- thisArg – переданный извне контекст this;
- arr[index] – текущий элемент массива;
- index – индекс элемента массива;
- arr – исходный массив;
- не забываем добавлять результат выполнения функции в контейнер.
- После завершения цикла возвращаем из функции map контейнер с модифицированными данными.
Метод filter
Суть этого метода заключается в его названии. Это один из самых простых способов отфильтровать массив в JavaScript. В остальном он также очень похож на все предыдущие.
Обязательные условия
- arr – массив, который надо отфильтровать;
- callbackFn – функция, которая содержит в себе проверку элемента массива условиям фильтрации:
- element – элемент массива возвращаемый в callback функцию;
- index – индекс этого элемента;
- array – исходный массив.
- thisArg – передаваемый в функцию контекст, это необязательный параметр.
Основная идея
Этот метод предназначен, чтобы фильтровать данные в массиве. Я привел в качестве примера случай, когда исходная коллекция содержит данные разных типов, но нам нужно извлечь только числа. Или же более живой пример: у нас есть массив с объектами, каждый из которых содержит информацию о каком-то пользователе. В каждом объекте указан год рождения. Наша задача – найти всех, кто родился до 2000 года. В этом случае мы просто передаем в метод filter функцию обратного вызова, в которой проверяем это условие, и, если оно выполняется, возвращаем true.
Решение
- Создаем функцию filter, указав в качестве параметров:
- arr – массив;
- callback – функция обратного вызова;
- thisArg – контекст this, это необязательный параметр.
- Внутри функции создаем контейнер, в который будем помещать отфильтрованные данные из массива;
- При помощи цикла for перебираем массив, где вызываем callback-функцию при помощи метода .call(), это нужно, чтобы передать в неё контекст this;
- В функцию обратного вызова передаем в параметры:
- thisArg – переданный извне контекст this;
- arr[index] – текущий элемент массива;
- index – индекс элемента массива;
- arr – исходный массив;
- если callback-функция возвращает true, то добавляем этот элемент массива в контейнер.
- После завершения цикла возвращаем из функции map контейнер с данными, удовлетворяющими условиям фильтрации.
Методы every and some
Эти два метода полезны для оценки массива на соответствие каким-либо условиям. Every проверяет, подходят ли этим условиям все элементы, а some – хотя бы один. Их реализации очень похожи, поэтому я решил объединить их в один подраздел.
Обязательные условия
- callbackFn – функция, обрабатывающая каждый элемент перебираемого массива. В случае every, если хотя бы один из элементов не соответствует условию, то возвращается false. При пустом массиве true. some работает по такому же принципу, но возвращает true, если хотя бы один элемент удовлетворяет проверке и false по умолчанию – то есть при пустом массиве и при несоответствии условиям:
- element – элемент массива, возвращаемый в callback-функцию;
- index – индекс этого элемента;
- array – исходный массив, который мы перебираем.
- thisArg – передаваемый в функцию контекст, это необязательный параметр.
Основная идея
Как исходит из описания выше, эти методы хорошо использовать при проверке массивов на выполнение каких-либо условий. Например, валидации. При помощи every мы можем проверить, все ли элементы подходят условиям, и в случае провала не отправлять данные на сервер. Для some можно представить такой пример: у нас есть несколько массивов с группами людей. Мы точно знаем, что кто-то из них болен и что в случае принадлежности больного к какому-то массиву всех их отправят на карантин. Мы можем просто перебрать каждый массив и проверить, если ли там хотя бы кто-то, кому нездоровится.
Решение
- Создаем функцию every, указав в качестве параметров:
- arr – массив;
- callback – функция обратного вызова;
- thisArg – контекст this, это необязательный параметр.
- При помощи цикла for перебираем массив, где вызываем callback-функцию при помощи метода .call(), это нужно, чтобы передать в неё контекст this;
- В функцию обратного вызова передаем в параметры:
- thisArg – переданный извне контекст this;
- arr[index] – текущий элемент массива;
- index – индекс элемента массива;
- arr – исходный массив;
- для every – если хотя бы один из вызовов функции обратного вызова вернул false, то и мы возвращаем false. Для some – если хотя бы один из вызовов функции обратного вызова вернул true, то и мы возвращаем true.
- Для every – по умолчанию мы всегда возвращаем из функции true. Для some – по умолчанию мы всегда возвращаем из функции false.
Метод reduce
Метод reduce() выполняет функцию обратного вызова, предоставленную пользователем, на каждом элементе массива по порядку, а также передает возвращаемое значение из вычисления на предыдущем элементе. Конечный результат работы метода над всеми элементами массива – одно значение.
Обязательные условия
- arr – массив, с которым мы будем работать;
- callbackFn – функция, обрабатывающая каждый элемент перебираемого массива:
- previousValue – элемент, возвращаемый предыдущим вызовом функции;
- currentValue – значение текущего элемента;
- index– индекс этого элемента.
- initialValue – изначальный элемент, который будет возвращен как previousValue при первом вызове callback. Если его не указать, то в качестве аккумулятора будет использован первый элемент перебираемого массива.
Основная идея
Смысл – обработать массив таким образом, чтобы на выходе получить одно значение. Классический пример – задача сложить все элементы массива и получить один ответ. Другим примером может быть объединение значений коллекции в одну строку – при том условии, что не все его элементы являются строками.
Решение
- Создаем функцию reduce, указав в качестве параметров:
- arr – массив;
- callback – функция обратного вызова;
- initialValue – переменная, используемая в качестве изначального значения аккумулятора при первом вызове функции.
- Объявляем переменную result, которую будем передавать на следующий этап;
- Если первый аргумент не был передан, то используем его в качестве первого элемента массива и не забываем удалить его из массива;
- Если первоначальное значение было передано, то делаем его значением результата;
- При помощи цикла for перебираем массив, где вызываем callback-функцию;
- В функцию обратного вызова передаем в параметры:
- null – нам не нужно передавать контекст this, поэтому вместо него передаем пустое значение;
- result – результат, который мы передаем в callback для совершения каких-либо манипуляций над ним;
- arr[index] – текущий элемент массива;
- arr – исходный массив;
- после выполнения функции обратного вызова присваиваем результат в переменную result.
- Возвращаем результат после полного прохода цикла.
Список литературы
В этой заметке я рассмотрел, как реализовать рабочие варианты уже существующих методов перебора массивов. В реальной работе вы в 100% случаев будете пользоваться их встроенными в язык аналогами, но способность их писать самостоятельно помогает лучше понять, как они работают и когда их выгодно использовать. Именно это и могут проверить на собеседовании. Я не стал добавлять в реализацию обработку ошибок и тесты. Чтобы узнать об этом больше, стоит обратиться к официальной документации. Ниже – список литературы к заметке.
- MDM Web Docs - For
- MDM Web Docs - this
- MDM Web Docs - Function.prototype.call()
- MDM Web Docs - Callback function
- MDM Web Docs - Array
- MDM Web Docs - Array.prototype.forEach()
- MDM Web Docs - Array.prototype.map()
- MDM Web Docs - Array.prototype.filter()
- MDM Web Docs - Array.prototype.every()
- MDM Web Docs - Array.prototype.some()
- MDM Web Docs - Array.prototype.reduce()
Напоследок
Если вам понравилась статья и вы хотите больше подобного контента, то ставьте лайки и подписывайтесь на мой блог и социальные сети. Буду рад подискутировать в комментариях! Всем спасибо!