Валерий Шестернин
Массивы в JavaScript представляют собой специальный тип объектов, в котором элементы упорядочены с помощью индексов. Различные движки JavaScript в каждой среде исполнения оптимизируют работу с массивами, поэтому разработчикам важно знать, какие методы массивов использовать для написания эффективного и понятного кода. В данной статье мы кратко рассмотрим эти методы и узнаем, как их применять.
Методы для работы с элементами массива
Поскольку массивы это упорядоченные коллекции, метод для добавления элемента в конец массива push
работает гораздо быстрее, чем метод для добавления элемента в начало unshift
, ведь при изменении первого элемента (с индексом 0) в массиве изменяются индексы всех остальных элементов (сдвигаются на 1), что требует дополнительных вычислений. Аналогичная ситуация с методом pop
, который удаляет и возвращает последний элемент, срабатывая гораздо быстрее, чем метод shift
, производящий те же манипуляции с первым элементом:
const myArr = new Array(10000000).fill("some value");
myArr.unshift("first"); //выполнится за 21 мс
myArr.push("last"); //выполнится меньше чем за 1 мс
console.log(myArr.shift()); //first
console.log(myArr.pop()); //last
Если нужно удалить элемент в другой части массива или добавить несколько элементов - на помощь приходит метод splice
, который принимает индекс начала изменений, количество удаляемых элементов (необязательный параметр, но если он равен 0 - нужно добавить хотя бы один новый элемент) и добавляемые элементы, после чего возвращает удаленные элементы, если такие есть. Метод toSpliced
делает все то же самое, но возвращает новый массив не изменяя старый. Конечно мы можем изменить элемент массива обратившись к нему по индексу и указать новое значение arr[index]=value
, с недавних пор можно даже указывать отрицательные значения индекса что бы выбрать нужный по порядку элемент с конца массива с помощью метода at
(arr.at(-1)
вернет последний элемент массива). Но нам не всегда нужно изменять исходный массив, а городить большие конструкции весь массив для изменения одного элемента не очень хочется. Для таких случаев есть метод with
, который принимает индекс элемента и его новое значение после чего возвращает новый массив с измененным элементом.
const myArr = [1, 2, 3, 4, 5];
const newArr = myArr.with(3, "with");
//изменит элемент с индексом 3 на новое значение и вернет новый массив
console.log(newArr); //[ 1, 2, 3, 'with', 5 ]
const splicedArr = myArr.toSpliced(0, 2, "to", "Spliced");
//удалится два первых элемента и добавит новые
console.log(splicedArr); //[ 'to', 'Spliced', 3, 4, 5 ]
const deleted = myArr.splice(2, 1, "splice");
//изменит изначальный массив удалив один элемент с индексом 2 и добавив новый
console.log(deleted); //[3]
console.log(myArr); //[ 1, 2, 'splice', 4, 5 ]
А если нам нужна только часть массива - его можно “порезать” методом slice
, который принимает необязательными параметрами первый и предпоследний индекс нужного “куска” (если передать отрицательные значения - отчет пойдет с конца массива) после чего возвращает новый массив, состоящий из выбранных элементов.
const myArr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const firstHalf = myArr.slice(0, 5);//[ 1, 2, 3, 4, 5 ]
const secondHalf = myArr.slice(5); //[ 6, 7, 8, 9, 10 ]
const lastTwo = myArr.slice(-2); //[ 9, 10 ]
Поиск по массиву
Самый удобный метод, позволяющий найти элемент массива так и называется find
. Он принимает функцию, проверяющую элемент на соответствие условиям поиска и возвращает элемент массива, соответствующий таким условиям или undefined
, если такого элемента нет. Для поиска не с начала, а с конца массива - есть метод findLast
. Сама функция принимает текущий элемент массива, индекс этого элемента и сам массив. Если же нам нужен не сам элемент, а его индекс, есть методы findIndex
и findLastIndex
, которые работают аналогично, но как уже ясно из названия, возвращают индекс первого или последнего соответственно элемента, подходящего по условиям. Когда не нужен ни индекс ни сам элемент, а просто информация о том что хотя один из элементов соответствует заданным условиям, на помощь приходит метод some
, а для проверки всех элементов - метод every
. Они так же принимают функцию проверки, но возвращают булево значение.
const users = [
{ id: 1, name: "John" },
{ id: 2, name: "Kate" },
{ id: 3, name: "John" },
];
users.find((user) => user.id === 2); //{ id: 2, name: "Kate" }
users.findIndex((user) => user.name === "John"); // 0
users.findLastIndex((user) => user.name === "John"); // 2
users.some(user => user.name === "Kate"); //true
users.every(user => user.name === "Kate"); //false
Если содержимое элемента известно и нужно найти его индекс, нам помогут методы indexOf
и lastIndexOf
которые принимают некоторое значение и возвращают соответственно индекс первого или последнего элемента равного этому значению. В случае отсутствия искомого элемента оба метода вернут -1
. Если нам нужен не индекс, а информация о наличии элемента в массиве - метод includes
так же примет значение искомого элемента и вернет ответ булевым значением. При этом важно помнить, что при поиске ссылочного типа данных нужно передавать именно его ссылку, а не описывать его. Если такой элемент не был описан в коде, лучше воспользоваться findIndex
или findLastIndex
.
const John = { id: 1, name: "John" };
const users = [John, { id: 2, name: "Kate" }, { id: 3, name: "Stan" }];
users.indexOf({ id: 3, name: "Stan" }); // -1
//элемент не найден потому что искомый объект
//описан в методе и это уже новый объект
users.includes(John); // true
//а здесь все отработает как надо
Являются ли получаемые данные массивом мы можем понять передав эти данные в метод isArray
глобального объекта Array
, который вернет результат проверки в виде булева значения.
Методы для преобразования массивов
Мы можем производить разные манипуляции с массивами. Метод concat
принимает любое количество массивов или отдельных элементов, после чего возвращает новый массив, состоящий из элементов изначального и всех переданных в метод, правда чаще для таких операций используют спред оператор (newArr = […arr1, …arr2, “some value”]
). Методом reverse
мы можем “перевернет” массив изменив порядок элементов, а toReversed
вернет новый массив с элементами исходного, но в обратном порядке.
const arr1 = [1, 2, 3];
const reversedArr = arr1.toReversed()//[3,2,1]
//создаст новый массив и исходный не изменится
const arr2 = [4, 5, 6];
const newArr = arr1.concat(arr2, 7, 8); //[1,2,3,4,5,6,7,8]
newArr.reverse() //[8,7,6,5,4,3,2,1]
//а этот метод изменит исходный массив
Превратить любой массив в строку поможет метод join
, который принимает разделитель между элементами в новой строке. Кроме того существует метод toLocalString
, который так же возвращает строку из элементов массива, но он принимает не разделитель, а настройки, например локализация или подобное и применяет собственный метод toLocalString
ко всем вложенным объектам, датам и цифрам в массиве.
const words = ['I', "love", 'JavaScript']
console.log(words.join(' ')); // I love JavaScript
const date = new Date("August 12, 2023 16:20:00");
const myArr = [1234.56, date];
console.log(myArr.toLocaleString()); // 1,234.56,8/12/2023, 4:20:00 PM
console.log(myArr.toLocaleString("ru-RU")); // 1 234,56,12.08.2023, 16:20:00
Отсортировать элементы массива по определенному условию можно двумя методами sort
и toSorted
. Каждый из них принимает необязательным параметром функцию сортировки, а если она не указана, массив сортируется в порядке, определенном значениями кодовых точек каждого символа Unicode. Чтобы выполнить это сравнение, каждый элемент преобразуется в строку. Отличие этих двух методов в том, что sort
изменяет изначальный массив, а toSorted
возвращает новый.
const myArr = [3, 5, 1, 2, 4];
const sortedArr = myArr.toSorted(); //[1,2,3,4,5]
//здесь создастся новый массив
console.log(myArr); //[3,5,1,2,4]
myArr.sort(); //[1,2,3,4,5]
//а здесь изменится исходный
const users = [
{ id: 3, name: "John" },
{ id: 1, name: "Kate" },
{ id: 2, name: "John" },
];
users.sort();
//не отсортирует массив потому что элементы
//просто преобразуются в строки
users.sort((a, b) => a.id - b.id);
//отстортирует массив по id пользователей
Мы можем создать новый массив из исходного, отсортировав его элементы с помощью метода filter
. По принципу работы он напоминает find
, но вместо возврата элемента он создает новый массив и наполняет его элементами исходного, удовлетворяющими переданное условие. Метод flat
так же возвращает новый массив, но изменяет уровень вложенности изначальных элементов на указанное значение (по умолчанию на один уровень). Так если элементом массива является другой массив, его элементы “поднимутся” и станут элементами нового массива.
const myArr = [[1, 2], [3, 4], [5, 6], 7];
const filteredArr = myArr.filter((item) => Array.isArray(item));
console.log(filteredArr); //[ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]
console.log(filteredArr.flat());//[ 1, 2, 3, 4, 5, 6 ]
Перебор массива и работа с функциями обратного вызова:
Для перебора массива существует два метода forEach
и map
. Оба принимают callback функцию, которая будет вызвана для каждого элемента массива, но map
вернет новый массив, в котором каждый элемент будет результатом исполнения функции, а forEach не возвращает ничего. Так же существует метом flatMap
, который, как понятно из названия, объединяет результат выполнения методов map
и flat
. При использовании данных методов важно помнить, что они не позволяют остановить перебор элементов массива. Если Вам не нужно перебирать весь массив - воспользуйтесь циклами for
, for...of…
или методами some
, find
и т.д.
let myArr = ["I love", "", "JavaScript"];
myArr.forEach((item) => console.log(item));
//выведет в консоль каждый элемент
myArr.map((item) => item.split(" "));
// [["I","love"],[""],["JavaScript"]]
myArr.flatMap((item) => item.split(" "));
// ["I","love", "", "JavaScript"]
Если в результате перебора массива нам нужно собрать определенные данные с элементов массива и преобразовать их в новое значение, с эти прекрасно справятся методы reduce
и reduceRight
. Они принимают callback функцию, которая вызывается для каждого элемента и необязательным параметром начальное значение аккумулятора в который собирается нужные нам данные. Сама функция может принимать текущий элемент, его индекс, значение аккумулятора и сам массив. Оба метода возвращают полученное значение аккумулятора, а отличаются тем, что reduce
проходится по массиву с его начала, а reduceRight
с конца.
const shoppingList = [
{ name: "potato", price: 10, quantity: 2 },
{ name: "cucumber", price: 15, quantity: 1 },
{ name: "tomato", price: 20, quantity: 0.5 },
];
const total = shoppingList.reduce(
(accumulator, item) => accumulator + item.price * item.quantity,
0); // Результат: 45
const numbers = [1, 2, 3, 4, 5];
const reversedJoin = numbers.reduceRight(
(accumulator, currentValue) => accumulator + currentValue,
"");
console.log(reversedJoin); // Результат: 54321
Заключение
В JavaScript существует более 40 методов для работы с массивами. Знание этих методов может помочь вам заменять громоздкие конструкции в коде на лаконичные строки с использованием соответствующих методов. Это сделает ваш код более производительным, а так же легким для чтения и понимания. Надеюсь данная статья была полезна для понимания столь важной темы.
Карта развития разработчика
Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile
Комментарии
0