Как копировать массив в js
JavaScript ES6: оператор расширения
Пример №1: вставка массивов в другие массивы
Взгляните на этот код. Тут оператор расширения не используется:
Вы думали, что так и будет?
Если этот код выполнить, то в результате будет выведено следующее:
Пример №2: математические вычисления
Как видите, если требуется найти максимальное значение нескольких чисел, Math.max() нужно несколько параметров. К сожалению, если надо найти максимальный элемент числового массива, сам массив методу Math.max() передать нельзя. До появления в JS оператора расширения самым простым способом поиска максимального элемента в массиве с помощью Math.max() было использование метода apply() :
Выше представлена работающая конструкция, но выглядит всё это не очень-то приятно.
А вот как то же самое делается с помощью оператора расширения:
Пример №3: копирование массивов
В JS нельзя скопировать массив, просто приравняв новую переменную той, которая уже содержит существующий массив. Рассмотрим пример:
Если его выполнить, можно увидеть следующее:
Надо заметить, что ничего страшного тут не происходит. Перед нами — стандартное поведение JS. А для того, чтобы действительно создать копию массива, можно воспользоваться оператором расширения. Вот пример использования этого оператора для копирования массивов. Код выглядит практически так же, как и в вышеприведённом примере. Однако здесь используется оператор расширения, применённый к исходному массиву, а вся эта конструкция помещена в квадратные скобки:
Выполнив этот код, можно увидеть, что выводит он то, чего мы от него и ожидаем:
Дополнительный пример: преобразование строки в массив
Напоследок — вот один интересный пример использования оператора расширения для преобразования строк в массивы. Здесь достаточно применить этот оператор к нужной строке, поместив всю эту конструкцию в пару квадратных скобок:
Итоги
Сегодня мы рассмотрели особенности работы с оператором расширения. Это — одна из новых возможностей JavaScript ES6, полезная мелочь, которая вполне способна улучшить читаемость кода и немного сократить его размер.
Уважаемые читатели! Пользуетесь ли вы оператором расширения в JavaScript?
Создать независимую копию JavaScript массива :: Хранитель заметок
Создать независимую копию JavaScript массива
В JavaScript все присваивания объектов реализуются через передачу ссылок на них.
Хоть мы и получили две разные переменные, но тем не менее они обе ссылаются на один и тот же объект массива. Если сейчас в одном массиве произвести какие-либо манипуляции с элементами, то аналогичные изменения можно будет увидеть и в другом.
Если вы хотите сделать независимую копию массива, то нужно использовать метод slice без аргументов.
Массивы oldArray и newArray будут состоять из одних и тех же элементов, но фактически это будут разные объекты.
Важно запомнить, что если массив состоит из сущностей Array или Object, то они по прежнему будут ссылаться на родительские объекты.
Ещё заметки со схожей тематикой
Категории
Коментарии к заметке
slice реально очень хорошо помагает если надо скопировать массива. При этом, если измениь первый массив, то второй останется без изменений.
Спасибо, искал долго, было много всего не по теме, а у вас сразу за пол странички все объяснили. Спасибо большое!
Спасибо огромное, очень помогло с решением одной проблемы!!
Атлант, тогда у этой задачи нет универсального решения.
Объекты можно «клонировать» с помощью метода Object.assign() (описание Object.assign() на MDN).
Но у этого метода как и slice() та же проблема — копируются только ссылки на вложенные объекты и массивы. Чтобы сделать глубокую копию всех данных, вам нужно рекурсивно вызывать соответствующие методы клонирования.
В некоторых JS-библиотеках эти алгоритмы уже реализованы. Можете воспользоваться cloneDeep из библиотеки Lodash.
А универсального решения задачи тут нет из-за того, что в ваших массивах и объектах могут храниться такие значения, для которых сложно или даже невозможно создать независимую копию, например, функции, DOM-узлы или объекты WeakMap.
Способ, который работает и в случае, когда в массиве есть массивы или объекты:
Способ работающий, но очень медленный. Я бы не стал его использовать в проектах.
Чуть мозг не поломал, пока не понял, что ссылки на объекты у скопированного массива действительно остаются прежними, спасибо за этот комментарий.
© 2009–2020 Владимир Кузнецов.
Все права защищены и принадлежат их владельцам.
Копирование материалов данного блога допускается только с разрешения автора.
Как копировать массив в js
Язык JavaScript предоставлет богатые возможности для работы с массивами, которые реализуются с помощью методов объекта Array. Рассмотрим применение этих методов
Копирование массива. slice()
Копирование массива может быть поверхностным или неглубоким (shallow copy) и глубоким (deep copy).
При неглубоком копировании достаточно присвоить переменной значение другой переменной, которая хранит массив:
В данном случае переменная people после копирования будет указывать на тот же массив, что и переменная users. Поэтому при изменении элементов в people, изменятся элементы и в users, так как фактически это один и тот же массив.
Такое поведение не всегда является желательным. Например, мы хотим, чтобы после копирования переменные указывали на отдельные массивы. И в этом случае можно использовать глубокое копирование с помощью метода slice() :
В данном случае после копирования переменные будут указывать на разные массивы, и мы сможем изменять их отдельно друг от друга.
Также метод slice() позволяет скопировать часть массива. Для этого он принимает два параметра:
Например, выберем в новый массив элементы, начиная с 1 индекса по индекс 4 не включая:
И поскольку индексация массивов начинается с нуля, то в новом массиве окажутся второй, третий и четвертый элемент.
Если указан только начальный индекс, то копирование выполняется до конца массива:
Метод push() добавляет элемент в конец массива:
Метод pop() удаляет последний элемент из массива:
shift()
Метод shift() извлекает и удаляет первый элемент из массива:
unshift()
Метод unshift() добавляет новый элемент в начало массива:
Удаление элемента по индексу. splice()
Метод splice() удаляет элементы с определенного индекса. Например, удаление элементов с третьего индекса:
Метод splice возвращает удаленные элементы в виде нового массива.
В данном случае удаление идет с начала массива. Если передать отрицательный индекс, то удаление будет производиться с конца массива. Например, удалим последний элемент:
Дополнительная версия метода позволяет задать количество элементов для удаления. Например, удалим с первого индекса три элемента:
Еще одна версия метода splice позволяет вставить вместо удаляемых элементов новые элементы:
В данном случае удаляем три элемента с 1-го индекса и вместо них вставляем два элемента.
concat()
Метод concat() служит для объединения массивов. В качестве результата он возвращает объединенный массив:
Метод join() объединяет все элементы массива в одну строку, используя определенный разделитель, который передается через параметр:
В метод join() передается разделитель между элементами массива. В данном случае в качестве разделителя будет использоваться точка с запятой и пробел («; «).
Метод sort() сортирует массив по возрастанию:
Стоит отметить, что по умолчанию метод sort() рассматривает элементы массива как строки и сортирует их в алфавитном порядке. Что может привести к неожиданным результатам, например:
Функция сортировки получает два рядом расположенных элемента массива. Она возвращает положительное число, если первый элемент должен находится перед вторым элементом. Если первый элемент должен располагаться после второго, то возвращается отрицательное число. Если элементы равны, возвращается 0.
reverse()
Метод reverse() переворачивает массив задом наперед:
Поиск индекса элемента
Методы indexOf() и lastIndexOf() возвращают индекс первого и последнего включения элемента в массиве. Например:
firstIndex имеет значение 0, так как первое включение строки «Tom» в массиве приходится на индекс 0, а последнее на индекс 3.
Проверка наличия элемента
В качестве второго параметра метод includes() принимает индекс, с которого надо начинать поиск:
В данном случае мы видим, что при поиске со 2-го индекса в массиве есть строка «Bob», тогда как начиная с 5-го индекса данная строка отсутствует.
Если если этот параметр не передается, то по умолчанию поиск идет с 0-го индекса.
При передаче отрицательного значения поиск идет с конца
every()
Метод every() проверяет, все ли элементы соответствуют определенному условию:
В метод every() в качестве параметра передается функция, которая представляет условие. Эта функция в качестве параметра принимает элемент и возвращает true (если элемент соответствует условию) или false (если не соответствует).
filter()
forEach() и map()
Методы forEach() и map() осуществляют перебор элементов и выполняют с ними определенный операции. Например, используем метод метода forEach() для вычисления квадратов чисел в массиве:
Консольный вывод программы:
Например, применим метод map к вычислению квадратов чисел массива:
Функция, которая передается в метод map() получает текущий перебираемый элемент, выполняет над ним операции и возвращает некоторое значение. Это значение затем попадает в результирующий массив squares
Поиск в массиве
Метод find() возвращает первый элемент массива, который соответствует некоторому условию. В качестве параметр метод find принимает функцию условия:
Метод findIndex также принимает функцию условия, только возвращает индекс первого элемента массива, который соответствует этому условию:
Метод flat и преобразование массива
Метод flat() упрощает массив с учетом указанной вложенности элементов:
То есть метод flat() фактически из вложенных массивов переводит элементы во внешний массив самого верхнего уровня. Однако мы видим, что элементы массива второго уровня вложенности перешли в массив первого уровня вложенности, но тем не менее по-прежнему находятся во вложенном массиве. Дело в том, что метод flat() по умолчанию применяется только к вложенным массивам первого уровня вложенности. Но мы можем передать в метод уровень вложености:
Если массив содержит вложенные массивы гораздо более глубоких уровней вложенности, или мы даже не знаем, какие уровни вложенности есть в массиве, но мы хотим, чтобы все элементы были преобразованы в один массив, то можно использовать значение Infinity :
Копировать массив или объект JS по правилам
Подробный разбор правильного копирования массивов и объектов с примерами кода на JavaScript.
Как только вы решили копировать массив или объект в JavaScript следует вспомнить о том, что массивы и объекты являются изменяемыми(mutable), в отличие от примитивных переменных и хранятся как ссылки.
Изменяемые, это те, состояние которых может быть изменено после их создания.
На практике это будет означать следующее:
Поэтому необходимо создавать копию данных, а не делать ссылку на существующие.
Копировать массив или объект в JS не так просто как кажется
Для этого необходимо не просто использовать знак равенства для присвоения новой переменной старых значений, а производить клонирование, иначе вы просто создадите ссылку и будете работать с исходным объектом.
Использовать функцию slice() для копирования массива
Эта функция используется для копирования части массива. Если не указывать параметры, то массив будет копироваться целиком.
Другой способ с использованием Array.from :
Если бы sheeps был бы объектом, то копировать следовало бы так:
Использовать Spread syntax чтобы копировать массив или объект
Spread syntax, появившийся в ES6, позволяет “вытаскивать” перебираемые элементы из своего контейнера.
Неглубокое копирование
Обратите внимание на то, что происходит клонирование только одного уровня – поверхностное копирование!
Если у вас многомерный массив для создания копии каждого уровня придется применить один из предыдущих способов к каждому уровню отдельно.
Если у вас смешанные данные, например, когда массив содержит объекты, в которых значениями могут быть тоже объекты или массивы, то следует использовать рекурсивную функцию, в которой вы будете проверять тип данных и в зависимости от этого копировать массив, объект или примитивную переменную.
Лучший способ клонировать многомерный массив или объект
На данный момент наиболее простым способом клонирования массивов и объектов является преобразование данных в строку, а за тем обратное преобразование в объект с помощью JSON :
Подводя итог
Статья подготовлена по материалам следующих источников:
Методы массивов
Массивы предоставляют множество методов. Чтобы было проще, в этой главе они разбиты на группы.
Добавление/удаление элементов
Мы уже знаем методы, которые добавляют и удаляют элементы из начала или конца:
splice
Как удалить элемент из массива?
Так как массивы – это объекты, то можно попробовать delete :
Поэтому для этого нужно использовать специальные методы.
Метод arr.splice(str) – это универсальный «швейцарский нож» для работы с массивами. Умеет всё: добавлять, удалять и заменять элементы.
Этот метод проще всего понять, рассмотрев примеры.
В следующем примере мы удалим 3 элемента и заменим их двумя другими.
Здесь видно, что splice возвращает массив из удалённых элементов:
Метод splice также может вставлять элементы без удаления, для этого достаточно установить deleteCount в 0 :
В этом и в других методах массива допускается использование отрицательного индекса. Он позволяет начать отсчёт элементов с конца, как тут:
slice
Он возвращает новый массив, в который копирует элементы, начиная с индекса start и до end (не включая end ). Оба индекса start и end могут быть отрицательными. В таком случае отсчёт будет осуществляться с конца массива.
concat
Метод arr.concat создаёт новый массив, в который копирует данные из других массивов и дополнительные значения.
Он принимает любое количество аргументов, которые могут быть как массивами, так и простыми значениями.
Если аргумент argN – массив, то все его элементы копируются. Иначе скопируется сам аргумент.
Обычно он просто копирует элементы из массивов. Другие объекты, даже если они выглядят как массивы, добавляются как есть:
Для корректной обработки в объекте должны быть числовые свойства и length :
Перебор: forEach
Метод arr.forEach позволяет запускать функцию для каждого элемента массива.
Например, этот код выведет на экран каждый элемент массива:
А этот вдобавок расскажет и о своей позиции в массиве:
Результат функции (если она вообще что-то возвращает) отбрасывается и игнорируется.
Поиск в массиве
Далее рассмотрим методы, которые помогут найти что-нибудь в массиве.
indexOf/lastIndexOf и includes
Методы arr.indexOf, arr.lastIndexOf и arr.includes имеют одинаковый синтаксис и делают по сути то же самое, что и их строковые аналоги, но работают с элементами вместо символов:
Кроме того, очень незначительным отличием includes является то, что он правильно обрабатывает NaN в отличие от indexOf/lastIndexOf :
find и findIndex
Представьте, что у нас есть массив объектов. Как нам найти объект с определённым условием?
Здесь пригодится метод arr.find.
Его синтаксис таков:
Функция вызывается по очереди для каждого элемента массива:
В реальной жизни массивы объектов – обычное дело, поэтому метод find крайне полезен.
filter
На тот случай, если найденных элементов может быть много, предусмотрен метод arr.filter(fn).
Преобразование массива
Перейдём к методам преобразования и упорядочения массива.
Метод arr.map является одним из наиболее полезных и часто используемых.
Он вызывает функцию для каждого элемента массива и возвращает массив результатов выполнения этой функции.
Например, здесь мы преобразуем каждый элемент в его длину:
sort(fn)
Вызов arr.sort() сортирует массив на месте, меняя в нём порядок элементов.
Не заметили ничего странного в этом примере?
По умолчанию элементы сортируются как строки.
Функция должна для пары значений возвращать:
Например, для сортировки чисел:
Теперь всё работает как надо.
Давайте возьмём паузу и подумаем, что же происходит. Упомянутый ранее массив arr может быть массивом чего угодно, верно? Он может содержать числа, строки, объекты или что-то ещё. У нас есть набор каких-то элементов. Чтобы отсортировать его, нам нужна функция, определяющая порядок, которая знает, как сравнивать его элементы. По умолчанию элементы сортируются как строки.
Кстати, если мы когда-нибудь захотим узнать, какие элементы сравниваются – ничто не мешает нам вывести их на экран:
В процессе работы алгоритм может сравнивать элемент с другими по нескольку раз, но он старается сделать как можно меньше сравнений.
На самом деле от функции сравнения требуется любое положительное число, чтобы сказать «больше», и отрицательное число, чтобы сказать «меньше».
Это позволяет писать более короткие функции:
Помните стрелочные функции? Можно использовать их здесь для того, чтобы сортировка выглядела более аккуратной:
Будет работать точно так же, как и более длинная версия выше.
reverse
Метод arr.reverse меняет порядок элементов в arr на обратный.
Он также возвращает массив arr с изменённым порядком элементов.
split и join
В примере ниже таким разделителем является строка из запятой и пробела.
У метода split есть необязательный второй числовой аргумент – ограничение на количество элементов в массиве. Если их больше, чем указано, то остаток массива будет отброшен. На практике это редко используется:
Вызов split(s) с пустым аргументом s разбил бы строку на массив букв:
reduce/reduceRight
Методы arr.reduce и arr.reduceRight похожи на методы выше, но они немного сложнее. Они используются для вычисления какого-нибудь единого значения на основе всего массива.
Функция применяется по очереди ко всем элементам массива и «переносит» свой результат на следующий вызов.
При вызове функции результат её вызова на предыдущем элементе массива передаётся как первый аргумент.
Этот метод проще всего понять на примере.
Тут мы получим сумму всех элементов массива всего одной строкой:
Давайте детальнее разберём, как он работает.
Поток вычислений получается такой:
В виде таблицы, где каждая строка –- вызов функции на очередном элементе массива:
sum | current | result | |
---|---|---|---|
первый вызов | 0 | 1 | 1 |
второй вызов | 1 | 2 | 3 |
третий вызов | 3 | 3 | 6 |
четвёртый вызов | 6 | 4 | 10 |
пятый вызов | 10 | 5 | 15 |
Здесь отчётливо видно, как результат предыдущего вызова передаётся в первый аргумент следующего.
Мы также можем опустить начальное значение:
Результат – точно такой же! Это потому, что при отсутствии initial в качестве первого значения берётся первый элемент массива, а перебор стартует со второго.
Таблица вычислений будет такая же за вычетом первой строки.
Но такое использование требует крайней осторожности. Если массив пуст, то вызов reduce без начального значения выдаст ошибку.
Поэтому рекомендуется всегда указывать начальное значение.
Метод arr.reduceRight работает аналогично, но проходит по массиву справа налево.
Array.isArray
Массивы не образуют отдельный тип языка. Они основаны на объектах.
Поэтому typeof не может отличить простой объект от массива:
Большинство методов поддерживают «thisArg»
Этот параметр не объяснялся выше, так как очень редко используется, но для наиболее полного понимания темы мы обязаны его рассмотреть.
Вот полный синтаксис этих методов:
Например, вот тут мы используем метод объекта army как фильтр, и thisArg передаёт ему контекст:
Итого
Шпаргалка по методам массива:
Для добавления/удаления элементов:
Для поиска среди элементов:
Для перебора элементов:
Для преобразования массива:
Изученных нами методов достаточно в 99% случаев, но существуют и другие.
Полный список есть в справочнике MDN.
На первый взгляд может показаться, что существует очень много разных методов, которые довольно сложно запомнить. Но это гораздо проще, чем кажется.
Внимательно изучите шпаргалку, представленную выше, а затем, чтобы попрактиковаться, решите задачи, предложенные в данной главе. Так вы получите необходимый опыт в правильном использовании методов массива.
Всякий раз, когда вам будет необходимо что-то сделать с массивом, а вы не знаете, как это сделать – приходите сюда, смотрите на таблицу и ищите правильный метод. Примеры помогут вам всё сделать правильно, и вскоре вы быстро запомните методы без особых усилий.
Задачи
Переведите текст вида border-left-width в borderLeftWidth
То есть дефисы удаляются, а все слова после них получают заглавную букву.