Кратко
СкопированоМассив — это структура, в которой можно хранить коллекции элементов — чисел, строк, других массивов и так далее. Элементы нумеруются и хранятся в том порядке, в котором их поместили в массив. Элементов может быть сколько угодно, они могут быть какими угодно.
Массивы очень похожи на нумерованные списки.
Как пишется
СкопированоСоздадим массив с помощью квадратных скобок [
.
К примеру, можно создать пустой массив:
const guestList = [] // 😭 гостей нет
const guestList = [] // 😭 гостей нет
А можно создать сразу с элементами внутри:
const theGirlList = [ 'Серсея', 'Илин Пейн', 'Меррин Трант', 'Дансен', 'Гора']
const theGirlList = [ 'Серсея', 'Илин Пейн', 'Меррин Трант', 'Дансен', 'Гора' ]
Элементы могут быть разных типов:
const infoArray = [ 'Россия', 'Москва', 144.5, 'Russian ruble', true]
const infoArray = [ 'Россия', 'Москва', 144.5, 'Russian ruble', true ]
Внутри массива могут быть другие массивы:
const arrayOfArrays = [ 'Россия', ['Москва', 'Санкт-Петербург', 'Казань', 'Екатеринбург'], [true, true, false, true]]
const arrayOfArrays = [ 'Россия', ['Москва', 'Санкт-Петербург', 'Казань', 'Екатеринбург'], [true, true, false, true] ]
Как понять
СкопированоМассивы хранят элементы в пронумерованных «ячейках». Нумерация начинается с нуля. Первый элемент массива будет иметь номер 0, второй — 1 и так далее. Номера называют индексами.
Количество доступных ячеек называют размером или длиной массива. В JavaScript длина массива обычно совпадает с количеством элементов в нем. Массивы хранят свой размер в свойстве length
:
const infoArray = [ 'Россия', 'Москва', 144.5, 'Russian ruble', true]console.log(infoArray.length)// 5
const infoArray = [ 'Россия', 'Москва', 144.5, 'Russian ruble', true ] console.log(infoArray.length) // 5
Чтение
СкопированоЧтобы получить содержимое ячейки с этим номером, обратитесь к конкретному индексу. Если ячейка пустая или такой ячейки нет, то JavaScript вернёт undefined
:
const guestList = ['Маша', 'Леонард', 'Шелдон', 'Джон Сноу']const firstGuest = guestList[0]console.log(firstGuest)// Машаconsole.log(guestList[3])// Джон Сноуconsole.log(guestList[999])// undefined
const guestList = ['Маша', 'Леонард', 'Шелдон', 'Джон Сноу'] const firstGuest = guestList[0] console.log(firstGuest) // Маша console.log(guestList[3]) // Джон Сноу console.log(guestList[999]) // undefined
Запись
СкопированоИспользуйте комбинацию чтения и оператора присваивания:
const episodesPerSeasons = [10, 10, 10, 10, 10, 9, 7, 6]console.log(episodesPerSeasons[5])// 9// Запись в ячейку с индексом 5episodesPerSeasons[5] = 10console.log(episodesPerSeasons[5])// 10
const episodesPerSeasons = [10, 10, 10, 10, 10, 9, 7, 6] console.log(episodesPerSeasons[5]) // 9 // Запись в ячейку с индексом 5 episodesPerSeasons[5] = 10 console.log(episodesPerSeasons[5]) // 10
Добавление элементов
СкопированоДобавление элементов — это частая операция. Для добавления используйте методы:
push
— для добавления в конец массива.( ) unshift
— для добавления в начало массива.( )
Оба принимают произвольное количество аргументов. Все аргументы будут добавлены в массив. Лучше использовать push
, он работает быстрее. Методы возвращают размер массива после вставки:
const watched = ['Властелин Колец', 'Гарри Поттер']watched.push('Зелёная Книга')console.log(watched)// ['Властелин Колец', 'Гарри Поттер', 'Зелёная книга']let newLength = watched.push('Мстители', 'Король Лев')console.log(newLength)// 5newLength = watched.unshift('Грязные танцы')console.log(newLength)// 6console.log(watched)// [// 'Грязные танцы', 'Властелин Колец', 'Гарри Поттер',// 'Зелёная книга', 'Мстители', 'Король Лев'// ]
const watched = ['Властелин Колец', 'Гарри Поттер'] watched.push('Зелёная Книга') console.log(watched) // ['Властелин Колец', 'Гарри Поттер', 'Зелёная книга'] let newLength = watched.push('Мстители', 'Король Лев') console.log(newLength) // 5 newLength = watched.unshift('Грязные танцы') console.log(newLength) // 6 console.log(watched) // [ // 'Грязные танцы', 'Властелин Колец', 'Гарри Поттер', // 'Зелёная книга', 'Мстители', 'Король Лев' // ]
Создать большой массив из чисел
СкопированоС помощью цикла и метода push
можно быстро создать большой массив с числами.
Создадим массив чисел от 1 до 1000:
const numbers = []for (let i = 1; i <= 1000; ++i) { numbers.push(i)}
const numbers = [] for (let i = 1; i <= 1000; ++i) { numbers.push(i) }
Создадим массив чётных чисел от 0 до 1000:
const evenNumbers = []for (let i = 0; i <= 1000; i += 2) { evenNumbers.push(i)}
const evenNumbers = [] for (let i = 0; i <= 1000; i += 2) { evenNumbers.push(i) }
💡 Поиск по массиву
СкопированоИспользуйте index
, чтобы найти, под каким индексом хранится элемент.
Используйте includes
, чтобы проверить, что элемент есть в массиве:
const episodesPerSeasons = [10, 10, 10, 10, 10, 9, 7, 6]console.log(episodesPerSeasons.includes(8))// falseconsole.log(episodesPerSeasons.includes(6))// true
const episodesPerSeasons = [10, 10, 10, 10, 10, 9, 7, 6] console.log(episodesPerSeasons.includes(8)) // false console.log(episodesPerSeasons.includes(6)) // true
Интересно, что если в массиве будут индексы с пропусками, то можно получить разрежённый массив. Предположим, у нас есть набор элементов:
const arr = ['d', 'o', 'k', 'a']
const arr = ['d', 'o', 'k', 'a']
Добавим к нему ещё один элемент, так, чтобы его индекс был больше длины всего набора элементов. Мы получим массив с незаполненным элементом (empty slot). Если обратимся к нему по индексу, получим undefined
:
arr[5] = '!'console.log(arr)// Выведет в Firefox ['d', 'o', 'k', 'a', <1 empty slot>, '!']console.log(arr[4])// Выведет undefined
arr[5] = '!' console.log(arr) // Выведет в Firefox ['d', 'o', 'k', 'a', <1 empty slot>, '!'] console.log(arr[4]) // Выведет undefined
Длина массива будет включать в себя все элементы, включая незаполненные, то есть в нашем случае не 5 элементов, а 6:
console.log(arr.length)// Выведет 6
console.log(arr.length) // Выведет 6
Или мы можем взять другой пример:
// Зрители, которые заняли три места в ряду. Индексы их мест: 0, 1, 2const audience = ['🐸', '🐶', '🐱']// Опоздавший зритель занял место в темноте и не по порядку!audience[5] = '🐌'// Места с индексами 3 и 4 всё ещё свободны, и это логично!console.log(audience)// Array(6) [ '🐸', '🐶', '🐱', <2 empty slots>, '🐌' ]
// Зрители, которые заняли три места в ряду. Индексы их мест: 0, 1, 2 const audience = ['🐸', '🐶', '🐱'] // Опоздавший зритель занял место в темноте и не по порядку! audience[5] = '🐌' // Места с индексами 3 и 4 всё ещё свободны, и это логично! console.log(audience) // Array(6) [ '🐸', '🐶', '🐱', <2 empty slots>, '🐌' ]
На практике
Скопированосоветует Скопировано
🛠 Копирование массива
С копированием есть хитрость. Массив — большая структура, и она не вмещается в одну переменную. Переменная хранит адрес, где находится массив. Если этого не знать, то результат такого кода будет выглядеть странно:
const iWatched = ['GameOfThrones', 'Breaking Bad']const vitalikWatched = iWatchedvitalikWatched.push('American Gods')console.log(iWatched)// ['GameOfThrones', 'Breaking Bad', 'American Gods'] 🤷♂️
const iWatched = ['GameOfThrones', 'Breaking Bad'] const vitalikWatched = iWatched vitalikWatched.push('American Gods') console.log(iWatched) // ['GameOfThrones', 'Breaking Bad', 'American Gods'] 🤷♂️
Хитрость в том, что во второй строке происходит копирование адреса, где находится массив, а не самого массива. В итоге получаем ситуацию, когда две переменные i
и vitalik
работают с одним массивом, так как хранят один адрес. Это особенность работы со значениями, которые хранятся по ссылке.
Копия массива создаётся с помощью метода slice
. Нужно вызвать его без аргументов и сохранить результат в новую переменную:
const iWatched = ['GameOfThrones', 'Breaking Bad']const vitalikWatched = iWatched.slice()vitalikWatched.push('American Gods')console.log(iWatched)// ['GameOfThrones', 'Breaking Bad'] 👍console.log(vitalikWatched)// ['GameOfThrones', 'Breaking Bad', 'American Gods'] 💪
const iWatched = ['GameOfThrones', 'Breaking Bad'] const vitalikWatched = iWatched.slice() vitalikWatched.push('American Gods') console.log(iWatched) // ['GameOfThrones', 'Breaking Bad'] 👍 console.log(vitalikWatched) // ['GameOfThrones', 'Breaking Bad', 'American Gods'] 💪
🛠 Деструктуризация массива
В современном JavaScript очень популярна деструктуризация массивов. Этот подход позволяет создавать переменные из элементов массива в одну строку:
const catProfile = [ 'Maru', 'Scottish Fold', true, 'https://youtu.be/ChignoxJHXc']
const catProfile = [ 'Maru', 'Scottish Fold', true, 'https://youtu.be/ChignoxJHXc' ]
В старом подходе, если из массива нужна пара значений, то их читают и сохраняют в переменные:
const catName = catProfile[0]const catBreed = catProfile[1]
const catName = catProfile[0] const catBreed = catProfile[1]
Новый подход делает то же самое, но короче:
const [name, breed] = catProfileconsole.log(name)// Maru
const [name, breed] = catProfile console.log(name) // Maru
На собеседовании
Скопировано отвечает
СкопированоОсновной сложностью данной задачи является определение уникальности элементов коллекции.
Попробуем для начала упростить задачу. Допустим, элементами коллекции являются примитивные значения, а сама коллекция - это массив. В этом случае наше решение может быть таким:
function getUnique (array) { // Если это не массив, возвращаем пустой массив if (Array.isArray(array) === false) { return [] } const uniqueArray = [] array.forEach(item => { if (uniqueArray.includes(item)) { return } uniqueArray.push(item) }) return uniqueArray}
function getUnique (array) { // Если это не массив, возвращаем пустой массив if (Array.isArray(array) === false) { return [] } const uniqueArray = [] array.forEach(item => { if (uniqueArray.includes(item)) { return } uniqueArray.push(item) }) return uniqueArray }
Сначала проверяем, что аргументом функции является массив, а затем перебираем коллекцию и накапливаем в новом массиве unique
только уникальные элементы. Для проверки уникальности используем метод массивов .includes
.
Проверим работу нашей функции:
const result = getUnique([1,2,2,1,3,0,2])console.log(result)// [ 1, 2, 3, 0 ]
const result = getUnique([1,2,2,1,3,0,2]) console.log(result) // [ 1, 2, 3, 0 ]
Функция работает как ожидалось, но такое решение не самое оптимальное. Если оценить сложность алгоритма, то окажется что она равна O(n^2). Для каждого элемента исходного массива необходимо осуществлять поиск в создаваемом массиве уникальных элементов.
JavaScript имеет специальный тип для создания уникальных коллекций — Set. С его помощью решение будет намного проще:
function getUnique (array) { // Если это не массив, возвращаем пустой массив if (Array.isArray(array) === false) { return [] } return [ ...new Set(array) ]}
function getUnique (array) { // Если это не массив, возвращаем пустой массив if (Array.isArray(array) === false) { return [] } return [ ...new Set(array) ] }
Это решение не требует перебора коллекции вручную (это произойдёт "под капотом"), так как при создании объекта Set
в него попадут только уникальные элементы переданого массива. Затем, используя деструктуризацию, мы преобразуем Set
обратно в массив.
По сравнению с предыдущим вариантом, это улучшает производительность, так как поиск в коллекции Set гарантировано выполняется быстрее чем при использовании .includes
.
А как быть с объектами в качестве элементов коллекции? С точки зрения JavaScript следующие элементы являются уникальными:
console.log(new Set([{},{},{},{}]))// Set(4) { {}, {}, {}, {} }
console.log(new Set([{},{},{},{}])) // Set(4) { {}, {}, {}, {} }
Если определяете «равенство» объектов, можно воспользоваться библиотекой Lodash. В библиотеку входит метод .is
, позволяющий сравнивать объекты:
function getUnique (array) { // Если это не массив, возвращаем пустой массив if (Array.isArray(array) === false) { return [] } const uniqueArray = [] array.forEach(item => { if (uniqueArray.some(uniqueItem => _.isEqual(item, uniqueItem))) { return } uniqueArray.push(item) }) return uniqueArray}
function getUnique (array) { // Если это не массив, возвращаем пустой массив if (Array.isArray(array) === false) { return [] } const uniqueArray = [] array.forEach(item => { if (uniqueArray.some(uniqueItem => _.isEqual(item, uniqueItem))) { return } uniqueArray.push(item) }) return uniqueArray }
Теперь перебираем элементы коллекции и накапливаем уникальные элементы в новом массиве unique
, используя для поиска повторений метод массивов .some
и метод библиотеки Lodash .is
. Сложность этого алгоритма равна O(n^2), так как требуется внутренний цикл для тестирования уникальности каждого элемента.
Можно улучшить гибкость нашего решения:
- адаптировать для использования не только массивов, но и других коллекций;
- устранить зависимость от библиотеки Lodash и дать возможность использовать собственную функцию для проверки уникальности элемента;
- снизить сложность алгоритма до O(n).
Для этого проверим что аргументом функции является итерируемый объект, а обход будем осуществлять с использованием for..of. Коллекцию уникальных элементов будем накапливать используя Map. Это позволит сохранять ключи значений для ускорения определения уникальности значений. Кроме этого, добавим возможность передавать в качестве второго аргумента функцию-компаратор. Функция должна вернуть пару ключ/значение, которые будут добавлены в коллекцию уникальных элементов.
Если функция не передана, вычисляем ключ, приводя значение к строке.
function getUnique (collection, comparator) { // Если это не итерируемая коллекция, возвращаем пустой массив if (collection === null || typeof collection[Symbol.iterator] !== 'function') { return [] } const unique = new Map() // Функция для получения пары ключ/значение const getKeyAndValue = typeof comparator === 'function' ? comparator : (item) => { const key = `${item}` return [key, item] } for (const item of collection) { const [key, value] = getKeyAndValue(item, unique) unique.set(key, value) } return Array.from(unique.values())}
function getUnique (collection, comparator) { // Если это не итерируемая коллекция, возвращаем пустой массив if (collection === null || typeof collection[Symbol.iterator] !== 'function') { return [] } const unique = new Map() // Функция для получения пары ключ/значение const getKeyAndValue = typeof comparator === 'function' ? comparator : (item) => { const key = `${item}` return [key, item] } for (const item of collection) { const [key, value] = getKeyAndValue(item, unique) unique.set(key, value) } return Array.from(unique.values()) }
Протестируем это решение без передачи компаратора:
console.log( getUnique([1, null, {}, [], {}, null]))// [ 1, null, {}, [] ]
console.log( getUnique([1, null, {}, [], {}, null]) ) // [ 1, null, {}, [] ]
Функция-компаратор пригодится для кастомизации логики, например если перед нами стоит задача объединить объекты на основе значения поля id
:
console.log( getUnique( [ { id: 2, type: 'd' }, { id: 3, category: null }, { name: 'Q', value: 42, id: 2 }, { width: 1024, size: 'big', id: 0 } ], (item, obj) => { const key = item.id const value = { ...obj.get(key), ...item } return [key, value] } ))// [// { id: 2, type: 'd', name: 'Q', value: 42 },// { id: 3, category: null },// { width: 1024, size: 'big', id: 0 }// ]
console.log( getUnique( [ { id: 2, type: 'd' }, { id: 3, category: null }, { name: 'Q', value: 42, id: 2 }, { width: 1024, size: 'big', id: 0 } ], (item, obj) => { const key = item.id const value = { ...obj.get(key), ...item } return [key, value] } ) ) // [ // { id: 2, type: 'd', name: 'Q', value: 42 }, // { id: 3, category: null }, // { width: 1024, size: 'big', id: 0 } // ]