Копировать элементы массива в другой массив


919

У меня есть массив 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

Ответы:


1249

Используйте функцию concat , вот так:

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

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



16
Я согласен, что исполнительное исполнение очень приятно. НО не является ли concat именно для этой цели для объединения с массивами? Так что должно быть стандартным . Или есть другие лучшие вещи, связанные с concat? И он может быть медленным только из-за плохой реализации движка JS браузера или где вы его используете? Это может быть исправлено в один прекрасный день. Я бы выбрал удобство сопровождения кода, а не оптимизацию скорости. Хм ....
Bitterblue

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

16
Как объединить два массива принятый ответ о том, как толкнуть один в другой ?! Это две разные операции.
Какако

@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прототип:

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это не плохо, скорее это просто неправильно понято и иногда неправильно. При этих обстоятельствах это только неправильно понято, но не неправильно.
Джек Гиффин

446

Я добавлю еще один ответ на будущее

В ECMAScript 6 вы можете использовать синтаксис Spread :

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

console.log(arr1)

Синтаксис распространения еще не включен во все основные браузеры. Текущую совместимость смотрите в этой (постоянно обновляемой) таблице совместимости .

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

редактировать:

См. Ответ Джека Гиффина ниже для получения дополнительной информации о производительности. Кажется, concat все еще лучше и быстрее, чем оператор распространения.


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 newArray = dataArray1.slice();
newArray.push.apply(newArray, dataArray2);

Так как «push» принимает переменное число аргументов, вы можете использовать applyметод pushфункции для выталкивания всех элементов другого массива. Он создает вызов для push, используя свой первый аргумент (здесь «newArray») в качестве «this» и элементы массива в качестве оставшихся аргументов.

Оператор slicein first получает копию первого массива, поэтому вы не должны изменять его.

Обновление Если вы используете версию 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 ║Spread ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ МОП / сек ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ разреженные массивы ║ДА! TheТолько 2 нарезанных ║ нет ║ Возможно 2   ║но ║
║ скудный ║ rayarray (1-й аргумент) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Поддержка ║ MSIE 4 ║ MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║ Edge 12 ║
║ ( источник ) ║NNav 4║NNav 4,06 ║ ║ NNav 4,06 NNav 3 ║ MSIE  NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║Массивоподобные действия║но ║Только толкаемые ║ ДА! ║ ДА! HaveЕсли есть ║
║like массив ║ ║array (2 - й Арг) ║ ║ ║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 всегда будет быстрее (в принципе, по крайней мере), потому что это просто копирование и вставка данных. Ниже приведена упрощенная наглядная диаграмма того, как может выглядеть реализация 32-битного массива (обратите внимание, что реальные реализации намного сложнее)

Байт ║ Данные здесь
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же
0x00 ║ int length = 0x00000001
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же
0x00 ║ int valueIndex = 0x00000000
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же
0x00 value int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (или там, где он находится в памяти)
0x01 ║ Там же
0x02 ║ Там же
0x03 ║ Там же

Как видно выше, все, что вам нужно сделать, чтобы скопировать что-то подобное, почти так же просто, как копировать его байт за байтом. С Array.prototype.push.apply это намного больше, чем просто копирование и вставка данных. «.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) (типизированный) массив, приведение его к массиву изменяет конструктор.
(function () {возвращать аргументы}) ()

Наблюдайте, что происходит, используя метод, который преобразует лайки массивов в массивы, такие как слайс.

  • ПРИМЕЧАНИЕ. Это плохая практика - вызывать slice для аргументов функции из-за производительности.

Наблюдайте за тем, что происходит, используя метод, который не преобразует подобные массивы в массивы, такие как concat.


9

В JavaScript ES6 вы можете использовать оператор ... в качестве оператора распространения, который по существу преобразует массив в значения. Затем вы можете сделать что-то вроде этого:

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

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

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


2
Если вы посмотрите, как babel преобразует его , вы увидите, что он не должен быть медленнее, чем при использовании 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)


-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.