Что нужно знать про массивы в JavaScript
Для прохода по массиву, поиску элементов, для сортировки и прочего, вероятнее всего, есть соответствующий метод массива. Разбираемся, какие они бывают.
57К открытий59К показов
Когда дело доходит до прохода по массиву, поиску элементов и так далее, вероятнее всего, для этого есть соответствующий метод массива. Однако часть методов остаётся в тени, поэтому в этой статье поговорим о полезных методах массивов в JavaScript.
Основа основ
Есть 4 вещи, которые вы должны знать при работе с массивами — это map
, filter
, reduce
и spread-оператор. Они являются мощным базисом.
map
Вероятнее всего, вы часто сталкивались с этим методом. Его используют, когда нужно поэлементно трансформировать массив в новый.
Метод map()
принимает всего один параметр — функцию, которая вызывается при каждой итерации по массиву. Метод возвращает новый массив, а не изменяет существующий.
Подобным образом можно получить массив, содержащий в себе только одно свойство объекта:
Поэтому, если нужно поэлементно трансформировать массив в новый — используйте map()
.
filter
В принципе имя метода говорит само за себя: он используется тогда, когда нужно произвести фильтрацию массива.
Как и map()
, метод filter()
принимает только один параметр — функцию, которая вызывается при каждой итерации. Функция должна возвращать булево значение:
true
— элемент остаётся в новом массиве,false
— элемент не остаётся в новом массиве.
После этого вы получаете отфильтрованный массив с нужными вам элементами.
К примеру, сохраним только нечётные числа в массиве:
Или же можно удалять строго определённые элементы массива:
reduce
Возможно, это довольно сложный для понимания метод. Но однажды вы поймёте, сколько крутых штук можно сделать с его помощью.
Метод reduce()
предназначен для комбинации значений массива в одно значение. Метод принимает два параметра. Первый из них — callback-функция (reducer
), второй — первичное значение, которое является необязательным и по-умолчанию является первым элементом массива. Callback-функция принимает 4 аргумента:
- accumulator (он хранит в себе промежуточный результат итераций),
- текущее значение массива,
- текущий
index
, - сам массив.
В большинстве случаев вам достаточно будет первых двух аргументов: промежуточного результата и текущего значения.
Поначалу это может звучать сложно, но на примерах всё разъяснится. Вот самый простой из них:
При первой итерации переменная total
(промежуточный результат) принимает значение 37. Возвращаемое значение равно (37 + n)
, где n равняется 12, т. е. значение равно 49. При второй итерации промежуточный результат равен 49 и к нему прибавляется 28. Теперь total
равен 77. И так далее.
Метод reduce()
настолько хорош, что с его помощью можно создавать остальные методы массива, например map()
или filter()
:
В случае map()
запускается функция, результат которой добавляется в конец accumulator’а с помощью spread-оператора. В filter()
почти то же самое, за исключением того, что на каждом элементе запускается filter-функция. Если эта функция возвращает true
, то возвращается предыдущий массив (промежуточное значение), иначе элемент добавляется в конец массива.
Вот более сложный пример: функция, которая «сглаживает» массив рода [1, 2, 3, [4, [[[5, [6, 7]]]], 8]]
в одномерный массив [1, 2, 3, 4, 5, 6, 7, 8]
.
Принцип работы схож с map()
, только с щепоткой рекурсии.
Spread оператор (стандарт ES2015)
Несмотря на то что оператор не является методом, с его помощью можно добиться многого при работе с массивами. Например, можно делать копии массивов или же объединять несколько массивов в один.
Обратите внимание, что этот оператор создаёт поверхностную копию исходного массива. Но что значит?
При поверхностном копировании элементы массива дублируются минимальным образом. Если, скажем, массив состоит только из примитивов (числа, строки, булевы значения), то это не вызовет никаких проблем. Но нельзя сказать то же самое о массиве объектов. При поверхностном копировании объектов в массиве созданные (продублированные) элементы ссылаются на оригиналы. Следовательно, если вы попытаетесь изменить объект в массиве, то изменения будут отражаться и на всех продублированных с него объектах (так же и в обратную сторону).
Поэтому если вам нужно сделать полную копию массива, содержащего объекты, то можно воспользоваться функцией cloneDeep из библиотеки lodash.
Углубляемся
Ниже будут описаны методы, которые могут оказаться чрезвычайно полезными в определённых ситуациях, например поиск элемента или извлечение подмассива из массива.
includes (стандарт ES2016)
Часто ли вы использовали indexOf()
, чтобы узнать о наличии чего-либо в массиве? Ужасный способ, не правда ли? К счастью, существует метод includes()
. Он принимает всего один параметр — искомый элемент — и возвращает true
/false
в зависимости от результата поиска.
concat
Этот метод объединяет два или более массивов.
forEach
Этот метод нужен, когда вам необходимо выполнить что-либо для каждого элемента массива. Метод принимает функцию в качестве параметра. Сама функция принимает 3 параметра: текущее значение, индекс и сам массив:
indexOf
Этот метод возвращает индекс первого вхождения элемента в массиве. indexOf()
так же часто используется в качестве проверки на наличие определённого элемента в массиве, хотя делать так не рекомендуется (ведь существует includes()
).
find
Метод find()
идентичен методу filter()
. Ему тоже нужно передавать функцию, которая проверяет каждый элемент массива. Но в отличие от filter()
, метод find()
прекращает поиск, когда находит первый элемент, удовлетворяющий условию проверяющей функции.
Используйте filter()
, чтобы пройтись по всему массиву, а find()
— чтобы найти уникальный элемент в нём.
findIndex
Метод полностью идентичен предыдущему методу find()
, за исключением того, что findIndex()
возвращает индекс конкретного искомого элемента.
Возможно вы скажете: «Эй! Этот метод же делает тоже самое, что и indexOf()
!».
Не совсем.
Передаваемый параметр в indexOf()
— это просто примитив (число, строка, булево значение, null, undefined или просто символ), в то время как параметр в findIndex()
— это callback-функция.
Поэтому если нужно найти индекс элемента в массиве примитивов, используйте indexOf()
. В других же случаях (массивы объектов) берите findIndex()
.
slice
Метод используется в тех случаях, когда необходимо извлечь подмассив из массива. Однако с ним нужно быть осторожным. Как и spread-оператор, этот метод возвращает поверхностную копию подмассива.
Как говорилось в начале статьи, во многих случаях циклы можно заменить.
Допустим, через API вы получили какое-то количество сообщений, но хотите отобразить только 5 из них. Ниже приведены 2 способа: первый — с использованием цикла, второй — с использованием slice
.
some
Используйте этот метод чтобы узнать, удовлетворяет ли условию хотя бы один из элементов массива. Как и map()
, fitler()
и find()
, метод some принимает callback-функцию как единственный параметр. Он возвращает true
при наличии в массиве хотя бы одного нужного элемента и false
— при отсутствии. Метод хорошо подходит для работы с разрешениями:
every
Идентичен предыдущему методу, но возвращает true
в случае, если все элементы проходят проверку (а не минимум один).
flat (стандарт ES2019)
Этот метод — новинка в мире JavaScript. flat()
создаёт новый массив из всех подмассивов в нём. Он принимает один параметр — глубину «сглаживания» массива:
flatMap (стандарт ES2019)
Исходя из названия, несложно догадаться, что делает этот метод.
Сначала он вызывает mapping-функцию для каждого элемента в массиве, а потом «выравнивает» их в один массив. И всё!
В примере выше нужно получить все слова из нескольких предложений. Вместо того, чтобы сначала использовать map()
для разделения предложения на слова, а потом соединять их в массив, проще сразу использовать flatMap()
.
А ещё помощью reduce()
можно подсчитать количество слов:
flatMap()
часто используется в Реактивном Программировании, например как вот здесь.
join
Используйте этот метод в том случае, если вам нужно создать строку, базируясь на элементах массива. Этот метод позволяет создать строку, соединив все элементы массива через указанный разделитель.
Так, например, можно отобразить список всех участников:
А вот более практичный способ. Допустим, сначала нужно отфильтровать список участников перед соединением их имён в строку:
from
Это статический метод, позволяющий создать новый массив из массиво-подобных и итерабельных объектов (строка). Метод полезен при работе с DOM.
Вы заметили, что в примере выше вместо объекта массива используется Array
? Именно поэтому метод from()
называется статическим.
С помощью forEach()
можно легко повесить на каждый элемент массива обработчик событий:
Модифицирующие методы
Ниже будет описана остальная часть методов массивов. Их отличия в том, что они модифицируют существующий массив, а не создают новый. В этом нет ничего плохого, просто нужно помнить про это.
В случае, если вам всё же нужно сохранить оригинальный массив, можно использовать поверхностное или глубокое копирование массива:
sort
Да, sort()
модифицирует оригинальный массив. По умолчанию, метод преобразует все элементы в строки и выполняет их сортировку в алфавитном порядке:
Поэтому будьте осторожны, придя, к примеру, с Python! При попытке выполнить sort
для массива чисел можно получить совсем не тот результат, какой хотелось бы:
Как же в таком случае отсортировать массив? Метод sort()
принимает всего один параметр — функцию сравнения. Эта функция принимает два параметра: первый элемент (например a
) и второй элемент (b
). Функция сравнения этих двух элементов должна возвращать число:
- отрицательное, если
a
должно стоять передb
, - положительное, если
b
должно стоять передa
, - ноль, если значения равны и не требуют перестановки.
И теперь массив чисел можно сортировать так:
Таким же образом можно сортировать даты (самые ранние):
fill
Метод fill()
изменяет или полностью заполняет массив с начальной по конечную позиции. Отличное применение — это заполнение нового массива одним статическим значением.
reverse
Здесь всё предельно ясно:
pop
Этот метод удаляет последний элемент массива и возвращает его.
Методы, которые можно заменить
В конце этой статьи будет рассказано о методах, которые образованы из вышеописанных методов и могут быть легко заменены чем-нибудь другим. Необязательно отказываться от этих методов, просто помните, что есть альтернатива.
push
При работе с массивами этот метод используется довольно часто. Фактически он добавляет один элемент в конец массива. Метод push()
также иногда используется для создания нового массива на основе уже существующего.
Если вам нужно создать новый массив на основе уже существующего (как в itemsIncremented
), то можно воспользоваться известными методами map()
, filter()
, reduce()
. Например, с помощью map()
создание нового массива выглядело бы так:
const itemsIncremented = todoItems.map(x => x + 1)
А если нужно будет добавить новый элемент в конец массива, кроме push()
можно использовать spread-оператор:
splice
Метод используется в тех случаях, когда нужно удалить элемент где-то в середине массива. Хотя тоже самое можно сделать и с помощью filter()
:
Всё бы хорошо, но как в таком случае удалить несколько элементов? Используя slice()
, конечно:
shift
Этот метод удаляет первый элемент массива и возвращает его. Подобного можно добиться с помощью spread
или rest
операторов:
unshift
С помощью этого метода можно добавлять элементы в начало массива. Как и в предыдущем случае, unshift()
можно заменить spread-оператором:
Важно помнить
- Каждый раз, когда нужно обработать массив, не обязательно использовать циклы или изобретать велосипед. Вероятнее всего, это уже сделали за вас. Поищите подходящий метод.
- В большинстве случаев задачу можно будет решить с помощью методов
map()
,filter()
,reduce()
или spread-оператора. - Никогда не помешает умение применять методы
slice()
,some()
,flatMap()
и тому подобные. Используйте их, когда это будет целесообразно. - Всегда помните, какие из методов создают новый массив, а какие модифицируют уже существующий. Иначе можно наломать дров.
- Метод
slice()
и spread-оператор делают поверхностную копию массива. Поэтому массивы и подмассивы будут ссылаться на один и тот же объект в памяти. - «Старые» методы, изменяющие массив, имеют современные аналоги. Тщательно выбирайте используемые методы.
57К открытий59К показов