От автора: все, что нужно знать о том, как работают в JavaScript массивы и мой любимый метод reduce(). Массивы – это аккуратный способ хранения непрерывных элементов в памяти в виде одной переменной. Элементы в массиве доступны по индексам. Индексы массива начинаются с 0.
Давайте создадим пустой массив разными способами.
let tasks = []; let projects = new Array();
Массивы в JS обозначаются квадратными скобками []. Взять длину массива или количество элементов в нем можно с помощью свойства length.
tasks.length; // returns 0; projects.length; // return 0;
В конструкторе Array можно задать начальную длину массива, чего нельзя сделать с массивом, созданным с помощью квадратных скобок.
Например:
let projects = new Array(10); console.log(projects.length); // Outputs 10
Рекомендуемый способ использования [] для создания массивов
Мы обсудим некоторые проблемы с производительностью, создание дыр в массиве, что приводит к снижение производительности и т.д. в последней части статьи.
Все примеры далее будут использовать [].
Давайте сохраним пару значений в массив и обратимся к ним по индексу
Заметка: в массиве можно хранить что угодно, но для лучшей производительности храните данные только одного типа в одном массиве. Например, массив текста, чисел, объектов и т.д. Не смешивайте типы (по крайней мере, старайтесь избегать этого).
let projects = ['Learn Spanish', 'Learn Go', 'Learn Erlang'];
Обратиться к элементам массива можно по индексу следующим образом.
console.log(projects[0]); // Outputs 'Learn Spanish' console.log(projects[2]); // Outputs 'Learn Erlang'; console.log(projects[3]); // Outputs 'undefined'
Добавление элементов в массив
Добавление элементов в конец массива (метод push)
Добавим пару элементов в конец массива. Для этого используем метод push().
projects.push("Learn Malayalam"); console.log(projects); // ["Learn Spanish", "Learn Go", "Learn Erlang", "Learn Malayalam"]
Заметка: метод push() мутирует массив. Для удаления элементов из массива используйте метод pop().
Добавление элементов в начало массива (unshift)
Добавим пару элементов в начало массива. Для этого используем метод unshift().
projects.unshift("Learn Tamil"); console.log(projects); // ["Learn Tamil", "Learn Spanish", "Learn Go", "Learn Erlang", "Learn Malayalam"]
Заметка: unshift() мутирует массив.
Чтобы добавить несколько элементов в начало массива, просто передайте необходимые аргументы в метод unshift.
projects.unshift("Learn French", "Learn Marathi"); console.log (projects); // ["Learn French", "Learn Marathi", "Learn Tamil", "Learn Spanish", "Learn Go", "Learn Erlang", "Learn Malayalam"]
Добавление элементов в начало массива ES6 (оператор расширения)
let projects = ['Learn Spanish', 'Learn Go', 'Learn Erlang']; projects = ['Learn Malayalam', ...projects]; console.log(projects); //Outputs-> ['Learn Malayalam', 'Learn Spanish', 'Learn Go', 'Learn Erlang']
Добавление элементов в конец массива ES6 (оператор расширения)
let projects = ['Learn Spanish', 'Learn Go', 'Learn Erlang']; projects = [...projects,'Learn Malayalam']; console.log(projects); //Outputs-> ['Learn Spanish', 'Learn Go', 'Learn Erlang','Learn Malayalam']
Заметка: способ с ES6 не мутирует массив и возвращает новый обновленный массив.
Удаление первого элементов из массива -> метод shift()
Метод shift() удаляет первый элемент из массива и возвращает его. Этот метод меняет длину массива. Если массив пуст, возвращается undefined.
let numbers = [1,2,3,4]; let firstNo = numbers.shift(); console.log(numbers); // [2,3,4]; console.log(firstNo); // 1
Удаление части массива, разрезание -> метод slice()
Slice method MDN Reference – довольно полезный метод, позволяющий отрезать массив с любой точки.
Метод slice() возвращает копию части массива в виде нового объекта массива с выбранными begin и end (end не включительно). Исходный массив не меняется.
arr.slice([begin[, end]])
Разберем несколько примеров.
var elements = ['Task 1', 'Task 2', 'Task 3', 'Task 4', 'Task 5']; elements.slice(2) //["Task 3", "Task 4", "Task 5"] elements.slice(2,4) // ["Task 3", "Task 4"] elements.slice(1,5) // ["Task 2", "Task 3", "Task 4", "Task 5"]
Удаление/добавление части массива -> метод splice()
Метод splice() меняет контент массива путем удаления существующих элементов и/или добавления новых. Используйте аккуратно, метод мутирует массив.
Полная документация MDN splice.
array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
Параметры
Индекс start – откуда начинается изменение массива (изначально 0).
deleteCount – (необязательный параметр) целое число, указывающее, сколько элементов старого массива необходимо удалить.
item1, item2, … (необязательные параметры) – элементы, которые необходимо удалить из массива, начиная со start. Если элементы не заданы, splice() удалит только элементы из массива.
Возвращаемое значение
Массив удаленных элементов. Если удален 1 элемент, возвращается массив из одного элемента. Если элементы не удалены, возвращается пустой массив.
var months = ['Jan', 'March', 'April', 'June']; months.splice(1, 0, 'Feb'); // inserts at 1st index position console.log(months); // expected output: Array ['Jan', 'Feb', 'March', 'April', 'June'] months.splice(4, 1, 'May'); // replaces 1 element at 4th index console.log(months); // expected output: Array ['Jan', 'Feb', 'March', 'April', 'May']
Удаление последнего элемента из массива -> метод pop()
Метод pop() удаляет последний элемент из массива и возвращает его. Метод меняет длину массива.
var elements = ['Task 1', 'Task 2', 'Task 3'];
Слияние двух массивов -> метод concat()
Метод concat() используется для слияния двух и более массивов. Метод не меняет существующие массивы и возвращает новый.
let array1 = ['a', 'b', 'c']; let array2 = ['d', 'e', 'f']; let merged = array1.concat(array2); console.log(merged); // expected output: Array ["a", "b", "c", "d", "e", "f"]
Слияние двух массивов ES6 (оператор расширения)
let array1 = ['a', 'b', 'c']; let array2 = ['d', 'e', 'f']; let merged = [...array1, ...array2]; console.log(merged); // expected output: Array ["a", "b", "c", "d", "e", "f"]
Объединение массивов -> метод join()
Метод join() объединяет все элементы массива (или массивоподобного объекта) в строку и возвращает ее. Очень полезный метод.
var elements = ['Task 1', 'Task 2', 'Task 3']; console.log(elements.join()); // expected output: Task 1,Task 2,Task 3 console.log(elements.join('')); // expected output: Task 1Task 2Task3 console.log(elements.join('-')); // expected output: Task 1-Task 2-Task 3
Вывод
Перебор массива в цикле
Существуют разные способы перебора массива в цикле. Разберем простой пример.
Перебор массива в цикле – цикл forEach
Цикл forEach принимает в качестве параметра функцию (обычную или стрелочную) и дает доступ к отдельному элементу массива в виде параметра этой функции. Функция принимает 2 параметра: первый – массив элементов, второй – индекс.
projects.forEach((e) => { console.log(e); });
projects.forEach(function (e) { console.log(e); });
Как получить доступ к индексу в forEach. Ниже я использую стрелочную функцию, но способ работает и для ES5 функций.
projects.forEach((e, index) => { console.log(e, index); });
Поиск элементов в массиве – метод find
Метод find() возвращает значение первого элемента в массиве, который удовлетворяет функции тестирования. В противном случае возвращается undefined. Синтаксис.
callback - функция, которая будет выполняться на всех значениях массив, принимает 3 параметра
***** element - текущий обрабатываемый элемент в массиве
***** index (необязательно) – индекс текущего элемента
***** array (необязательно) – массив, над которым выполняется поиск.
thisArg (необязательно) – объект, который будет использоваться для this при выполнении колбека.
Возвращаемое значение
Если элемент прошел тест, значение в массиве, иначе undefined.
arr.find(callback[, thisArg]) var data = [51, 12, 8, 130, 44]; var found = data.find(function(element) { return element > 10; }); console.log(found); // expected output: 51
Перебор массива – цикл for in
Цикл for…in работает только с перечисляемыми свойствами. Так как массивы перечисляемые, поэтому он работает с ними.
Цикл перебирает все перечисляемые свойства самого объекта и объектов, наследуемых от прототипа конструктора (свойства, расположенные ближе к объекту в цепочке прототипа, переписывают свойства прототипа).
Более подробно на MDN for in.
for(let index in projects) { console.log(projects[index]); }
Заметьте, что в коде сверху каждый раз в цикле создается переменная index.
Перебор массива в цикле – функция map
Функция map() позволяет трансформировать массив в новый объект и вернуть новый массив на основе переданной функции.
Это очень мощный метод в руках JS разработчика.
Заметка: map всегда возвращает одно и то же количество выводных данных, но он может изменить тип этих данных. Например, если массив содержит 5 элементов, map вернет 5 трансформированных элементов.
let num = [1,2,3,4,5]; let squared = num.map((value, index, origArr) => { return value * value; });
Функция, передаваемая в map, принимает 3 параметра.
squared — новый возвращаемый массив
num — массив, на котором будет запускаться функция map
value — текущее обрабатываемое значение
index — текущий индекс обрабатываемого значения
origArr — оригинальный массив
Map() – пример 1 – простой
let num = [1,2,3,4,5]; let squared = num.map((e) => { return e * e; }); console.log(squared);
В коде сверху мы пробегаем по всем элементам в массиве и создаем новый массив со значениями в квадрате. Вывод
Map() – пример 2 – простая трансформация
Возьмем входной объектный литерал и трансформируем его в пару ключ/значение.
Например, возьмем массив ниже.
let projects = ['Learn Spanish', 'Learn Go', 'Code more'];
И трансформируем его в пару ключ/значение, как показано ниже.
{ 0: "Learn Spanish", 1: "Learn Go", 2: "Code more" }
Код верхней трансформации с выводом.
let newProjects = projects.map((project, index) => { return { [index]: project } }); console.log(newProjects);
Map() – пример 3 – возвращаемый поднабор данных
Разберем пример ниже
let tasks = [ { "name": "Learn Angular", "votes": [3,4,5,3] }, { "name": "Learn React", "votes": [4,4,5,3] }, ];
Нам нужно вывести name от tasks. Реализация:
let taskTitles = tasks.map((task, index, origArray) => { return { name: task.name } }); console.log(taskTitles);
И вывод.
Перебор массива в цикле – функция filter
Filter возвращает поднабор массива. Метод полезен, когда нужно найти записи в коллекции записей. Колбек функция для filter должна возвращать true или false. Значение true включает запись в новый массив, а false исключает запись из нового массива.
Возвращается новый массив. Разберем пример массива.
let tasks = [ { "name": "Learn Angular", "rating": 3 }, { "name": "Learn React", "rating": 5 }, { "name": "Learn Erlang", "rating": 3 }, { "name": "Learn Go", "rating": 5 }, ];
Давайте с помощью filter найдем все tasks с rating 5. Код и результат.
let tasks5 = tasks.filter((task) => { return task.rating === 5; }); console.log(tasks5);
И вывод.
В функции filter у нас всего 1 значение, поэтому ее можно укоротить до следующего вида:
tasks.filter(task => task.rating === 5);
Заметка: функция filter не трансформирует вывод в новый массив.
Перебор массива в цикле – функция reduce
Функция reduce проходит в цикле по массиву и может вернуть уменьшенный набор. Это очень мощная функция. Мне кажется, мощнее других методов работы с массивами (хотя у каждого метода своя роль).
Метод reduce() (из MDN) применяет функцию к аккумулятору и каждому элементу в массиве (слева направо), чтобы уменьшить его до одного значения.
Reduce – простой пример 1
const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // expected output: 10 // 5 + 1 + 2 + 3 + 4 console.log(array1.reduce(reducer, 5)); // expected output: 15
Заметка: при первом вызове колбека accumulator и currentValue могут быть одним из двух значений. Если в вызов reduce() передали initialValue, то accumulator будет равен initialValue, а currentValue будет равно первому значению в массиве. Если initialValue нет, то accumulator будет равен первому значению массива, а currentValue второму.
Мощь reduce() заключается в том, что с его помощью можно реализовать свои map(), find() и filter().
Использование reduce для разбиения данных на категории
Например, у вас есть структура данных ниже, которую вам нужно разбить на категории male и female.
let data = [ {name: "Raphel", gender: "male"}, {name: "Tom", gender: "male"}, {name: "Jerry", gender: "male"}, {name: "Dorry", gender: "female"}, {name: "Suzie", gender: "female"}, {name: "Dianna", gender: "female"}, {name: "Prem", gender: "male"}, ];
Нам нужно, чтобы вывод был следующим:
{"female" : [ {name: "Dorry", gender:"female"}, {name: "Suzie", gender: "female"}, {name: "Dianna", gender: "female"}, ], "male" : [ {name: "Raphel", gender:"male"}, {name: "Tom", gender:"male"}, {name: "Jerry", gender:"male"}, {name: "Prem", gender:"male"}, ] }
Посмотрим код и поймем, как разбить данные на такие категории.
let genderwise = data.reduce((acc,item, index) => { acc[item.gender].push(item); return acc; }, {male: [], female:[]}); console.log(genderwise);
Важный момент в коде выше – функцию reduce можно инициализировать со стартовым аккумулятором любого типа. В примере сверху это объектный литерал.
{ male: [], female: []}
Надеюсь, это точно демонстрирует мощь reduce.
Реализация кастомной функции map() с помощью reduce
function map(arr, fn) { return arr.reduce((acc, item) => [...acc, fn(item)], []); }
Сверху показана реализация кастомной функции map. В функцию сверху мы передаем пустой массив [] как первое значение для аккумулятора. Функция reduce возвращает новый массив со значениями из расширенного аккумулятора и добавляет результат выполнения колбек функции с текущим элементом.
Давайте посмотрим на использование и вывод.
Реализация кастомной функции filter() с помощью reduce
Реализуем метод filter() с помощью reduce().
function filter (array, fn) { return arr.reduce(function (acc, item, index) { if (fn(item, index)) { acc.push(item); } return acc; },[]); }
Посмотрим, как использовать код выше и его вывод. Я мог бы переписать оригинальный Array.prototype.filter, но делаю так, чтобы не манипулировать встроенными методами.
Внутри кастомного фильтра выполняем функцию reduce и добавляем только те элементы к аккумулятору, которые совпадают с предикатом, который возвращает колбек в фильтр.
Реализация функции foreach с помощью reduce
Разберем реализацию своей функции foreach.
function forEach(arr, fn) { arr.reduce((acc, item, index) => { item = fn(item, index); }, []); }
По сравнению с другими методами, реализация очень простая. Мы просто берем переданный массив, выполняем reduce и возвращаем текущий элемент, как результат выполнения колбека с текущим элементом и индексом.
Пример использования и вывод.
Дыры в массивах
Дыры в массивах – это пустые элементы. Они могут появиться из-за ряда операций типа удаления или других операций, которые случайно оставили дыры.
Сейчас иметь дыры в массиве плохо с точки зрения производительности. Разберем пример.
let num = [1,2,3,4,5]; // No holes or gaps delete num[2]; // Creates holes console.log (num); [1, 2, empty, 4, 5]
Не используйте delete на массиве, если не знаете точно, что делаете. Метод delete не меняет длину массива. Избежать дыр в массиве можно с помощью методов splice(), pop() или shift().
Изменение длины массива и дыр
Вы можете быстро изменить длину массива.
let num = [1,2,3,4,5]; // length = 5; num.length = 3; // change length to 3 //The below logs outputs // [1,2,3] -> The last two elements are deleted console.log(num);
Если теперь увеличить длину, образуется дыра.
let num = [1,2,3,4,5]; num.length = 10; // increase the length to 10 console.log(num); // See holes here
Автор: Rajesh Pillai
Источник: https://codeburst.io/
Редакция: Команда webformyself.