Скопіюйте елементи масиву в інший масив


916

У мене є масив JavaScript, dataArrayякий я хочу натиснути на новий масив newArray. За винятком того, що я не хочу newArray[0]бути dataArray. Я хочу натиснути всі елементи в новий масив:

var newArray = [];

newArray.pushValues(dataArray1);
newArray.pushValues(dataArray2);
// ...

а ще краще:

var newArray = new Array (
   dataArray1.values(),
   dataArray2.values(),
   // ... where values() (or something equivalent) would push the individual values into the array, rather than the array itself
);

Тож тепер новий масив містить усі значення окремих масивів даних. Чи існує така скорочена стенда, pushValuesщоб мені не доводилося переглядати кожного окремого dataArray, додаючи елементи по черзі?



Це має бути відповідь davidwalsh.name/combining-js-arrays
starikovs

Чи відповідає це на ваше запитання? Як об’єднати два масиви в JavaScript та
дедублювати

Відповіді:


1246

Використовуйте функцію concat , наприклад:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayA.concat(arrayB);

Значення newArrayбуде [1, 2, 3, 4]( arrayAі arrayBзалишається незмінним; concatстворює та повертає новий результат для результату).



15
Я погоджуюсь, що виконавське виконання дуже приємне. АЛЕ не конкретно саме для цієї мети призначати масиви? Тож воно повинно бути стандартним . Або є інші кращі речі, пов'язані з конфатом? І це може бути повільним лише через погану реалізацію двигуна JS браузера або там, де ви його використовуєте? Це може бути зафіксовано одного дня. Я б вибрав ремонтопридатність коду через оптимізацію швидкої швидкості. Хм ....
Bitterblue

3
Також я просто орієнтував ситуацію: concat vs. push.apply. Google Chrome: швидкий (concat = переможець) Opera,: швидкий (concat = переможець) ,: IEповільніше (concat = переможець) Firefox,: повільний (push.apply = переможець, але в 10 разів повільніше, ніж конкомат Chrome) ... говорять про погану реалізацію двигуна JS .
Bitterblue

15
Як поєднує два масиви прийняту відповідь для того, як просунути один в інший ?! Це дві різні операції.
kaqqao

@kaqqao, тому що pushне вирівняє масив значень. concatдосягає того, що вимагає питання.
WiseGuyEh

644

Якщо ваші масиви не є величезними (див. Попередження нижче), ви можете використовувати push()метод масиву, до якого ви бажаєте додати значення. push()може приймати декілька параметрів, тому ви можете використовувати його apply()метод для передачі масиву значень, що підлягають натисканню, як список параметрів функції. Це має перевагу перед використанням concat()додавання елементів до масиву замість створення нового масиву.

Однак, схоже, що для великих масивів (порядку 100 000 членів і більше) цей трюк може провалитися . Для таких масивів кращим підходом є використання циклу. Докладніше дивіться на https://stackoverflow.com/a/17368101/96100 .

var newArray = [];
newArray.push.apply(newArray, dataArray1);
newArray.push.apply(newArray, dataArray2);

Ви можете узагальнити це у функції:

function pushArray(arr, arr2) {
    arr.push.apply(arr, arr2);
}

... або додати його до Arrayпрототипу 's:

Array.prototype.pushArray = function(arr) {
    this.push.apply(this, arr);
};

var newArray = [];
newArray.pushArray(dataArray1);
newArray.pushArray(dataArray2);

... або емуляція оригінального push()методу, дозволяючи кілька параметрів, використовуючи той факт, що concat(), наприклад push(), дозволяє декілька параметрів:

Array.prototype.pushArray = function() {
    this.push.apply(this, this.concat.apply([], arguments));
};

var newArray = [];
newArray.pushArray(dataArray1, dataArray2);

Ось базована на циклі версія останнього прикладу, підходить для великих масивів та всіх основних браузерів, включаючи IE <= 8:

Array.prototype.pushArray = function() {
    var toPush = this.concat.apply([], arguments);
    for (var i = 0, len = toPush.length; i < len; ++i) {
        this.push(toPush[i]);
    }
};

9
Примітка: newArray.push.apply(newArray, dataArray1);дає те саме, щоArray.prototype.push.applay(newArray,dataArra1);

Чи сумісна це з більш старими браузерами?
Дамієн Ó Сілла


1
@ DamienÓCeallaigh: Так. Це все сумісно з браузерами, які повертаються до IE 6 і навіть раніше.
Тім Даун

Це втілення поганих практик програмування. Array.prototype.concatце не погано, скоріше просто неправильно зрозуміло і іноді неправильно використовується. За цих обставин він лише неправильно розуміється, але не не використовується.
Джек Гіффін

444

Я додам ще одну відповідь "стійкого до майбутнього"

У ECMAScript 6 можна використовувати синтаксис Spread :

let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);

console.log(arr1)

Синтаксис розповсюдження ще не включений у всі основні веб-переглядачі. Про поточну сумісність див. Цю (постійно оновлювану) таблицю сумісності .

Однак ви можете використовувати синтаксис поширення з Babel.js .

редагувати:

Дивіться відповідь Джека Гіффіна нижче, щоб отримати додаткові коментарі щодо ефективності. Здається, ламати все-таки краще і швидше, ніж оператор розповсюдження.


7
Ви також можете використовувати оператор розповсюдження, якщо ви використовуєте TypeScript. Якщо ви націлите на ES5, він буде компілюватися до newArray.apply(newArray, dataArray1).
AJ Richardson

5
Примітка: якщо вам потрібен результат у третьому масиві (таким чином, не змінюючи arr1, як здавалося, вимагає початкового запитання), ви можете зробити newArray = [... arr1, ... arr2]
dim

О, я цього не знав. Дякую. Я додав коментар до редагування.
Карел Білек

Подібно до того, як функціонуватиме concat тоді, бонус за стійкість (вимкнення вихідного масиву).
Роб

@robertmylne Оператор розповсюдження очевидно не модифікує вихідний масив, натомість створює нову копію всього деспазованого вмісту масивів.
Джек Гіффін

145

Знайшов елегантний спосіб від MDN

var vegetables = ['parsnip', 'potato'];
var moreVegs = ['celery', 'beetroot'];

// Merge the second array into the first one
// Equivalent to vegetables.push('celery', 'beetroot');
Array.prototype.push.apply(vegetables, moreVegs);

console.log(vegetables); // ['parsnip', 'potato', 'celery', 'beetroot']

Або ви можете скористатися spread operatorфункцією ES6:

let fruits = [ 'apple', 'banana'];
const moreFruits = [ 'orange', 'plum' ];

fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]

1
Це не те запитання. Питання полягає в тому, щоб кожен раз створювати новий масив, а не змінювати старий масив.
Джек Гіффін

13
var a=new Array('a','b','c');
var b=new Array('d','e','f');
var d=new Array('x','y','z');
var c=a.concat(b,d)

Чи вирішує це ваша проблема?


13

Мені здається найпростішим:

var newArray = dataArray1.slice();
newArray.push.apply(newArray, dataArray2);

Оскільки "push" приймає змінну кількість аргументів, ви можете використовувати applyметод pushфункції для виштовхування всіх елементів іншого масиву. Він будує виклик для натискання, використовуючи перший аргумент (тут "newArray") як "цей", а елементи масиву - як решта аргументів.

sliceУ першій заяві отримує копію першого масиву, так що ви не зміните його.

Оновлення Якщо ви використовуєте версію javascript з доступним фрагментом, ви можете спростити pushвираз до:

newArray.push(...dataArray2)

1
Тут також згадується в MDN як приклад для "Об'єднання двох масивів"
Wilt

12

У наведеній нижче функції не виникає проблем із довжиною масивів і працює краще, ніж усі запропоновані рішення:

function pushArray(list, other) {
    var len = other.length;
    var start = list.length;
    list.length = start + len;
    for (var i = 0; i < len; i++ , start++) {
        list[start] = other[i];
    }
}

на жаль, jspref відмовляється приймати мої подання, тому тут вони є результатами, використовуючи benchmark.js

        Name            |   ops/sec   |  ± %  | runs sampled
for loop and push       |      177506 |  0.92 | 63
Push Apply              |      234280 |  0.77 | 66
spread operator         |      259725 |  0.40 | 67
set length and for loop |      284223 |  0.41 | 66

де

для циклу і натискання:

    for (var i = 0, l = source.length; i < l; i++) {
        target.push(source[i]);
    }

Натисніть Застосувати:

target.push.apply(target, source);

оператор розповсюдження:

    target.push(...source);

і нарешті, «задана довжина і для циклу» є вищевказаною функцією


Це питання шукає спосіб створювати новий масив щоразу, а не змінювати існуючий масив.
Джек Гіффін

10

𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗥𝗲𝘀𝘂𝗹𝘁𝘀

Для фактів проводиться перевірка працездатності на jsperf та перевірка деяких речей у консолі. Для проведення досліджень використовується веб-сайт irt.org . Нижче наводиться збірка всіх цих джерел разом із прикладом функції внизу.

╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ метод ║Concat║slice & push.apply ║ push.apply x2 ║ ForLoop pread розповсюдження ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / Sec ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Рідкі масиви ║ ТАК! Тольки нарізаний ║ ні ║ Можливо, 2   ║но ║
║ збережений рідкий ray ║array (1-й аргумент) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Підтримка ║MSIE 4║MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║Edege 12 ║
║ ( джерело ) ║NNav 4║NNav 4.06 ║ NNav 4.06 ║ NNav 3 ║ MSIE  NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║Различні дії║но ║Тольки натиснуті ║ ТАК! ║ ТАК! FЯкщо є ║
Подібно до масиву ║ ║array (2nd arg) ║ ║ ║iterator 1   ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Якщо об’єкт, схожий на масив, не має властивості Symbol.iterator , то спробуйте
  щоб поширити його, викине виняток.
2 Залежить від коду. У наведеному нижче прикладі код "ТАК" зберігає рідкість.
function mergeCopyTogether(inputOne, inputTwo){
   var oneLen = inputOne.length, twoLen = inputTwo.length;
   var newArr = [], newLen = newArr.length = oneLen + twoLen;
   for (var i=0, tmp=inputOne[0]; i !== oneLen; ++i) {
        tmp = inputOne[i];
        if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;
    }
    for (var two=0; i !== newLen; ++i, ++two) {
        tmp = inputTwo[two];
        if (tmp !== undefined || inputTwo.hasOwnProperty(two)) newArr[i] = tmp;
    }
    return newArr;
}

Як було сказано вище, я б заперечував, що Concat майже завжди є шлях як для продуктивності, так і для здатності зберегти рідкість запасних масивів. Тоді, для подібних масивів (наприклад, таких як DOMNodeLists document.body.children), я б рекомендував використовувати цикл for, оскільки він є другим найбільш ефективним і єдиним іншим методом, який зберігає розріджені масиви. Нижче ми швидко перейдемо до того, що мається на увазі під рідкісними масивами та схожими масивами, щоб усунути плутанину.

𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲

Спочатку деякі люди можуть подумати, що це підступ і що з часом постачальники браузерів зможуть оптимізувати Array.prototype.push, щоб бути досить швидким, щоб перемогти Array.prototype.concat. НЕ ПРАВО! Array.prototype.concat завжди буде швидше (в принципі принаймні), тому що це проста копія-n-вставка над даними. Нижче наводиться спрощена переконливо-візуальна схема того, як може виглядати 32-розрядна реалізація масиву (зверніть увагу на те, що реальні реалізації є багато складнішими)

Байт ║ Дані тут
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ там само
0x02 ║ там само
0x03 ║ там само
0x00 ║ int довжина = 0x00000001
0x01 ║ там само
0x02 ║ там само
0x03 ║ там само
0x00 ║ int valueIndex = 0x00000000
0x01 ║ там само
0x02 ║ там само
0x03 ║ там само
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ там само
0x02 ║ там само
0x03 ║ там само
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (або де б це не було в пам'яті)
0x01 ║ там само
0x02 ║ там само
0x03 ║ там само

Як видно з вище, все, що вам потрібно зробити, щоб скопіювати щось подібне майже так само просто, як і скопіювати його в байт. З Array.prototype.push.apply це набагато більше, ніж проста копія-n-вставка над даними. ".Apply" повинен перевірити кожен індекс у масиві та перетворити його в набір аргументів, перш ніж передавати його Array.prototype.push. Тоді Array.prototype.push повинен додатково виділяти більше пам'яті кожного разу, і (для деяких реалізацій браузера), можливо, навіть перераховувати деякі дані пошуку позиції для розрідженості.

Альтернативний спосіб його думати - це такий. Джерельний масив один - це велика стопка паперів, зшита разом. Джерельний масив два - це ще одна велика стопка паперів. Чи буде вам швидше це зробити

  1. Заходьте в магазин, купуйте достатню кількість паперу, необхідного для копії кожного вихідного масиву. Потім перенесіть кожен пачок паперового масиву через копіювальну машину і з'єднайте отримані дві копії разом.
  2. Зайдіть у магазин, придбайте достатньо паперу для однієї копії першого масиву джерел. Потім скопіюйте масив джерела на новий папір вручну, забезпечивши заповнення будь-яких порожніх мізерних плям. Потім поверніться до магазину, придбайте достатньо паперу для другого масиву джерел. Потім перейдіть через другий масив джерела і скопіюйте його, не забезпечивши порожніх пробілів у копії. Потім зшийте всі скопійовані папери разом.

У наведеній вище аналогії варіант №1 представляє Array.prototype.concat, а # 2 являє Array.prototype.push.apply. Спробуємо це перевірити за допомогою аналогічного JSperf, який відрізняється лише тим, що цей тестує методи над розрідженими масивами, а не суцільними масивами. Можна знайти тут .

Тому я хочу заперечувати, що майбутня продуктивність для цього конкретного випадку використання лежить не в Array.prototype.push, а в Array.prototype.concat.

𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀

𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀

Коли певні члени масиву просто відсутні. Наприклад:

// This is just as an example. In actual code, 
// do not mix different types like this.
var mySparseArray = [];
mySparseArray[0] = "foo";
mySparseArray[10] = undefined;
mySparseArray[11] = {};
mySparseArray[12] =  10;
mySparseArray[17] = "bar";
console.log("Length:   ", mySparseArray.length);
console.log("0 in it:  ", 0 in mySparseArray);
console.log("arr[0]:   ", mySparseArray[0]);
console.log("10 in it: ", 10 in mySparseArray);
console.log("arr[10]   ", mySparseArray[10]);
console.log("20 in it: ", 20 in mySparseArray);
console.log("arr[20]:  ", mySparseArray[20]);

Крім того, javascript дозволяє легко ініціалізувати запасні масиви.

var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];

𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀

Масив-подібний є об'єктом, який має принаймні lengthвластивість, але не був ініціалізований з new Arrayабо []; Наприклад, наведені нижче об'єкти класифікуються як подібні до масиву.

{0: "foo", 1: "bar", довжина: 2}
document.body.children
новий Uint8Array (3)
  • Це схоже на масив, оскільки хоча це (n) (набраний) масив, примушування до масиву змінює конструктор.
(функція () {повернути аргументи}) ()

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

  • ПРИМІТКА: Неправильна практика викликати фрагмент на аргументах функції через ефективність.

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


9

За допомогою JavaScript ES6 ви можете використовувати оператор ... як оператор розповсюдження, який по суті перетворить масив у значення. Потім ви можете зробити щось подібне:

const myArray = [1,2,3,4,5];
const moreData = [6,7,8,9,10];

const newArray = [
  ...myArray,
  ...moreData,
];

Хоча синтаксис є стислим, я не знаю, як це працює внутрішньо і які наслідки для продуктивності мають великі масиви.


2
Якщо ви подивитесь, як перетворює вавило , ви побачите, що це не повинно бути повільніше, ніж використання Array.push.applyтехніки.
emil.c

1
@JackGiffin Я тільки мав на увазі те, що згадував Райан, що він не знає, як це працює всередині та які наслідки для продуктивності, я насправді не пропонував такий підхід. У будь-якому випадку, ви зробили дуже хорошу роботу над своєю відповіддю, приємне дослідження, завжди добре знати такі деталі.
emil.c

9

Ось спосіб ES6

var newArray = [];
let dataArray1 = [1,2,3,4]
let dataArray2 = [5,6,7,8]
newArray = [...dataArray1, ...dataArray2]
console.log(newArray)

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

    let dataArray1 = [1,2,3,4]
    let dataArray2 = [5,6,7,8]
    let newArray = dataArray1.concat(dataArray2);
    console.log(newArray)


8

Є ряд відповідей, які говорять про Array.prototype.push.apply . Ось наочний приклад:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
Array.prototype.push.apply(newArray, dataArray1); // newArray = [1, 2]
Array.prototype.push.apply(newArray, dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]

Якщо у вас синтаксис ES6:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
newArray.push(...dataArray1); // newArray = [1, 2]
newArray.push(...dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]


4

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

var source = [1, 2, 3];
var range = [5, 6, 7];
var length = source.push(...range);

console.log(source); // [ 1, 2, 3, 5, 6, 7 ]
console.log(length); // 6

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

/**
 * Adds the items of the specified range array to the end of the source array.
 * Use this function to make sure only items of the same type go in the source array.
 */
function addRange<T>(source: T[], range: T[]) {
    source.push(...range);
}

2

У нас є два масиви a і b. код, що тут зробив - це масив, значення висувається в масив b.

let a = [2, 4, 6, 8, 9, 15]

function transform(a) {
    let b = ['4', '16', '64']
    a.forEach(function(e) {
        b.push(e.toString());
    });
    return b;
}

transform(a)

[ '4', '16', '64', '2', '4', '6', '8', '9', '15' ]

1
Будь ласка, не просто поштовий індекс, як відповідь. Поясніть, що робить код і як він вирішує проблему.
Патрік Гунд

0

Спробуйте це:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayB.reduce((pre, cur) => [...pre, ...cur], arrayA);
console.log(newArray)

-2

замість функції push () використовуйте функцію concat для IE. наприклад,

var a=a.concat(a,new Array('amin'));

1
обидва дуже сумісні з IE
Джек Гіффін

-3

Це робочий код, і він прекрасно працює:

var els = document.getElementsByTagName('input'), i;
var invnum = new Array();
var k = els.length;
for(i = 0; i < k; i++){invnum.push(new Array(els[i].id,els[i].value))}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.