Копировать элементы массива в другой массив - PullRequest
807 голосов
/ 11 ноября 2010

У меня есть массив 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, добавляя элементы по одному?

Ответы [ 14 ]

1084 голосов
/ 11 ноября 2010

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

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

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

618 голосов
/ 11 ноября 2010

Если ваши массивы не велики (см. Предостережение ниже), вы можете использовать метод 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: </p>

Array.prototype.pushArray = function() {
    var toPush = this.concat.apply([], arguments);
    for (var i = 0, len = toPush.length; i < len; ++i) {
        this.push(toPush[i]);
    }
};
338 голосов
/ 15 июня 2015

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

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

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

console.log(arr1)

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

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

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

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

138 голосов
/ 11 сентября 2015

Найден элегантный способ из 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"]
13 голосов
/ 11 ноября 2010
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)

Это решит вашу проблему?

12 голосов
/ 11 апреля 2016

Мне кажется самым простым следующее:

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

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

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

Обновление Если вы используете версию javascript с доступным слайсом, вы можете упростить выражение push до:

newArray.push(...dataArray2)
9 голосов
/ 03 июля 2017

Есть несколько ответов, говорящих о 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]
8 голосов
/ 20 мая 2017

Функция ниже не имеет проблемы с длиной массивов и работает лучше, чем все предлагаемые решения:

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);

и, наконец, «заданная длина и цикл» - это вышеуказанная функция

7 голосов
/ 29 июня 2016

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

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

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

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

5 голосов
/ 15 августа 2018

???????? ??? ???????

На самом деле, тест производительности на jsperf и проверка некоторых вещей в консоли выполняются. Для исследования используется сайт irt.org . Ниже собрана коллекция всех этих источников, а также пример функции внизу.

╔═══════════════╦══════╦═════════════════╦═══════════════╦═════════╦══════════╗
║ Method        ║Concat║slice&push.apply ║ push.apply x2 ║ ForLoop ║Spread    ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║ mOps/Sec      ║179   ║104              ║ 76            ║ 81      ║28        ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║ Sparse arrays ║YES!  ║Only the sliced  ║ no            ║ Maybe<sup>2<sub>  </sub></sup> ║no        ║
║ kept sparse   ║      ║array (1st arg)  ║               ║         ║          ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║ Support       ║MSIE 4║MSIE 5.5         ║ MSIE 5.5      ║ MSIE 4  ║Edge 12   ║
║ (<a href="https://www.irt.org/xref/core_objects.htm" rel="nofollow noreferrer">source</a>)      ║NNav 4║NNav 4.06        ║ NNav 4.06     ║ NNav 3  ║<strike>MSIE</strike> <strike>NNav</strike> ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║Array-like acts║no    ║Only the pushed  ║ YES!          ║ YES!    ║If have   ║
║like an array  ║      ║array (2nd arg)  ║               ║         ║iterator<sup>1<sub>  </sub></sup>║
╚═══════════════╩══════╩═════════════════╩═══════════════╩═════════╩══════════╝
<sup>1</sup> If the array-like object does not have a <i>Symbol.iterator</i> property, then trying
  to spread it will throw an exception.
<sup>2</sup> Depends on the code. The following example code "YES" preserves sparseness.
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-битного массива (обратите внимание, что реальные реализации намного сложнее)

Byte ║ Data here
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int length = 0x00000001
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueIndex = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (or whereever it is in memory)
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid

Как видно выше, все, что вам нужно сделать, чтобы скопировать что-то подобное, почти так же просто, как копировать его байт за байтом. С 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", length:2}
document.body.children
new Uint8Array(3)
  • Это похоже на массив, потому что, хотя это (n) (типизированный) массив, приведение его к массиву изменяет конструктор.
(function(){return arguments})()

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

var slice = Array.prototype.slice;
// For arrays:
console.log(slice.call(["not an array-like, rather a real array"]));
// For array-likes:
console.log(slice.call({0: "foo", 1: "bar", length:2}));
console.log(slice.call(document.body.children));
console.log(slice.call(new Uint8Array(3)));
console.log(slice.call( function(){return arguments}() ));
  • ПРИМЕЧАНИЕ: плохая практика - разрезать аргумент функции из-за производительности.

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

var empty = [];
// For arrays:
console.log(empty.concat(["not an array-like, rather a real array"]));
// For array-likes:
console.log(empty.concat({0: "foo", 1: "bar", length:2}));
console.log(empty.concat(document.body.children));
console.log(empty.concat(new Uint8Array(3)));
console.log(empty.concat( function(){return arguments}() ));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...