Оригінальна публікація: How to clone an array in JavaScript

В JavaScript є багато способів зробити що завгодно. Зараз ми будемо працювати з масивами.

1. Оператор Spread (поверхнева копія)

З того часу, як з’явився ES6 — це найпопулярніший метод. Це короткий синтаксис, який є неймовірно корисним під час використання таких бібліотек, як React і Redux.

numbers = [1, 2, 3];
numbersCopy = [...numbers];

Примітка: ненадійно копіює багатовимірні масиви. Значення масиву/об’єкта копіюються за посиланням, а не за значенням.

Це добре:

numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] та [1, 2, 3, 4]
// числа залишаються без змін

Це не добре:

nestedNumbers = [[1], [2]];
numbersCopy = [...nestedNumbers];

numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1, 300], [2]]
// [[1, 300], [2]]
// Обоє були змінені, оскільки мають спільні посилання

2. Старий добрий цикл for() (поверхнева копія)

Я думаю, що цей підхід найменш популярний, враховуючи, наскільки трендовим стало функціональне програмування в наших колах.

Чистий чи нечистий, декларативний чи імперативний, він виконує роботу!

numbers = [1, 2, 3];
numbersCopy = [];

for (i = 0; i < numbers.length; i++) {
  numbersCopy[i] = numbers[i];
}

Примітка: ненадійно копіює багатовимірні масиви. Оскільки ви використовуєте оператор =, він призначатиме об’єкти/масиви за посиланням, а не за значенням.

Це добре:

numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] та [1, 2, 3, 4]
// числа залишаються без змін

Це не добре:

nestedNumbers = [[1], [2]];
numbersCopy = [];

for (i = 0; i < nestedNumbers.length; i++) {
  numbersCopy[i] = nestedNumbers[i];
}

numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1, 300], [2]]
// [[1, 300], [2]]
// Обоє були змінені, оскільки мають спільні посилання

3. Старий добрий цикл while() (поверхнева копія)

Такий самий, що й for: нечистий, імперативний, бла-бла-бла… Але працює!?

numbers = [1, 2, 3];
numbersCopy = [];
i = -1;

while (++i < numbers.length) {
  numbersCopy[i] = numbers[i];
}

Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.

Це добре:

numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] та [1, 2, 3, 4]
// числа залишаються без змін

Це не добре:

nestedNumbers = [[1], [2]];
numbersCopy = [];

i = -1;

while (++i < nestedNumbers.length) {
  numbersCopy[i] = nestedNumbers[i];
}

numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1, 300], [2]]
// [[1, 300], [2]]
// Обоє були змінені, оскільки мають спільні посилання

4. Array.map (поверхнева копія)

На сучасній території ми знайдемо функцію map. Закорінена в математиці map — це концепція перетворення множини в множину іншого типу зі збереженням структури.

Простими словами: Array.map повертає масив тої самої довжини кожного разу.

Щоб подвоїти список чисел, використайте map із функцією double.

numbers = [1, 2, 3];
double = (x) => x * 2;

numbers.map(double);

А як щодо клонування??

Правда, ця публікація про клонування масивів. Щоб дублювати масив, просто поверніть елемент у виклику map.

numbers = [1, 2, 3];
numbersCopy = numbers.map((x) => x);

Якщо ви хочете побути математиками, (x) => x називається тотожністю. Вона повертає будь-який параметр, який було надано.

map(identity) клонує список.

identity = (x) => x;
numbers.map(identity);
// [1, 2, 3]

Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.

5. Array.filter (поверхнева копія)

Ця функція повертає масив, як і map, але не гарантовано, що він буде такої ж довжини.

А якщо відфільтрувати парні числа?

[1, 2, 3].filter((x) => x % 2 === 0);
// [2]

Довжина вхідного масиву дорівнює 3, а вихідного — 1.

Проте, якщо предикат вашого filter завжди повертає true, ви отримаєте дублікат!

numbers = [1, 2, 3];
numbersCopy = numbers.filter(() => true);

Кожен елемент проходить тест, тому він повертається.

Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.

6. Array.reduce (поверхнева копія)

Мені ніяково використовувати reduce для клонування масиву, тому що вона набагато потужніша за це. Але раз ви просите…

numbers = [1, 2, 3];

numbersCopy = numbers.reduce((newArray, element) => {
  newArray.push(element);

  return newArray;
}, []);

reduce перетворює початкове значення, коли воно проходить список.

В прикладі початкове значення є порожнім масивом і ми заповнюємо його кожним елементом на ходу. Цей масив повинен бути повернений з функції, щоб його використати в наступній ітерації.

Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.

7. Array.slice (поверхнева копія)

slice повертає поверхневу копію масиву на основі наданого вами початкового/кінцевого індексу.

Якщо ми хочемо перші 3 елементи:

[1, 2, 3, 4, 5].slice(0, 3);
// [1, 2, 3]
// Починається з індексу 0, закінчується на індексі 3

Якщо ми хочемо всі елементи, не вказуємо жодних параметрів:

numbers = [1, 2, 3, 4, 5];
numbersCopy = numbers.slice();
// [1, 2, 3, 4, 5]

Примітка: це поверхнева копія, тому вона також призначає об’єкти/масиви за посиланням, а не за значенням.

8. JSON.parse та JSON.stringify (глибока копія)

JSON.stringify перетворює об’єкт на рядок.

JSON.parse перетворює рядок на об’єкт.

Якщо їх поєднати, то можна перетворити об’єкт на рядок, а потім змінити процес навпаки, щоб створити нову структуру даних.

Примітка: надійно копіює глибоко вкладені об’єкти/масиви!

nestedNumbers = [[1], [2]];
numbersCopy = JSON.parse(JSON.stringify(nestedNumbers));

numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);

// [[1], [2]]
// [[1, 300], [2]]
// Два абсолютно окремі масиви!

9. Array.concat (поверхнева копія)

concat поєднує масиви зі значеннями або іншими масивами.

[1, 2, 3].concat(4); // [1, 2, 3, 4]
[1, 2, 3].concat([4, 5]); // [1, 2, 3, 4, 5]

Якщо ви не надасте нічого або порожній масив, повернеться поверхнева копія.

[1, 2, 3].concat(); // [1, 2, 3]
[1, 2, 3].concat([]); // [1, 2, 3]

Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.

10. Array.from (поверхнева копія)

Може перетворити будь-який ітерований об’єкт в масив. Надання масиву повертає поверхневу копію.

numbers = [1, 2, 3];
numbersCopy = Array.from(numbers);
// [1, 2, 3]

Примітка: також призначає об’єкти/масиви за посиланням, а не за значенням.

Висновок

Ну, було весело?

Я спробував клонувати, використовуючи лише 1 крок. Ви знайдете ще багато інших способів, якщо використаєте декілька методів та технік.