Кратко
СкопированоМассив — это структура, в которой можно хранить коллекции элементов — чисел, строк, других массивов и так далее. Элементы нумеруются и хранятся в том порядке, в котором их поместили в массив. Элементов может быть сколько угодно, они могут быть какими угодно.
Массивы очень похожи на нумерованные списки.
Как пишется
СкопированоСоздадим массив с помощью квадратных скобок [
.
К примеру, можно создать пустой массив:
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 } // ]