Как расширить существующий массив JavaScript другим массивом, не создавая новый массив

Похоже, что нет способа расширить существующий массив JavaScript другим массивом, то есть эмулировать метод Python extend.

Я хочу добиться следующего:

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
>>> a
[1, 2, 3, 4, 5]

Я знаю метод a.concat(b), но он создает новый массив вместо простого расширения первого. Мне бы хотелось, чтобы алгоритм работал эффективно, когда a значительно больше, чем b (т.е. тот, который не копирует a).

Примечание: Это не является дубликатом Как добавить что-либо в массив?. Цель здесь - добавить все содержимое одного массива в другой и сделать это. "на месте", т.е. без копирования всех элементов расширенного массива.

Ответ 1

Метод .push может принимать несколько аргументов. Вы можете использовать оператор распространения, чтобы передать все элементы второго массива в качестве аргументов .push:

>>> a.push(...b)

Если ваш браузер не поддерживает ECMAScript 6, вместо этого вы можете использовать .apply :

>>> a.push.apply(a, b)

Или, может быть, если вы думаете, что это понятнее:

>>> Array.prototype.push.apply(a,b)

Обратите внимание, что все эти решения завершатся с ошибкой, если массив b слишком длинный (проблема начинается с примерно 100 000 элементов, в зависимости от браузера). Если вы не можете гарантировать, что b достаточно короткий, вам следует использовать стандартную методику на основе петель, описанную в другом ответе.

Ответ 2

Обновление 2018 года: Лучший ответ - мой новый: a.push(...b). Не одобряйте этот вопрос, так как он никогда не отвечал на этот вопрос, но это был хак 2015 года вокруг первого попадания в Google :)


Для тех, кто просто искал "Расширение массива JavaScript" и попал сюда, вы можете очень хорошо использовать Array.concat.

var a = [1, 2, 3];
a = a.concat([5, 4, 3]);

Concat вернет копию нового массива, поскольку запускатель потоков не хотел. Но вам может быть все равно (конечно, для большинства видов использования это будет хорошо).


Для этого также есть хороший сахар ECMAScript 6 в виде оператора распространения:

const a = [1, 2, 3];
const b = [...a, 5, 4, 3];

(также копирует.)

Ответ 3

Вы должны использовать технику на основе цикла. Другие ответы на этой странице, основанные на использовании .apply, могут не работать для больших массивов.

Довольно краткая реализация, основанная на циклах:

Array.prototype.extend = function (other_array) {
    /* You should include a test to check whether other_array really is an array */
    other_array.forEach(function(v) {this.push(v)}, this);
}

Затем вы можете сделать следующее:

var a = [1,2,3];
var b = [5,4,3];
a.extend(b);

DzinX answer (используя push.apply) и другие методы, основанные на .apply, терпят неудачу, когда добавляемый нами массив большой (тесты показывают, что для меня большой размер> 150 000 записей в Chrome и> 500 000 записей в Firefox). Вы можете увидеть эту ошибку в этом jsperf.

Ошибка возникает из-за превышения размера стека вызовов при вызове Function.prototype.apply с большим массивом в качестве второго аргумента. (В MDN есть примечание об опасностях превышения размера стека вызовов при использовании Function.prototype.apply - см. раздел "Применение и встроенные функции".)

Для сравнения скорости с другими ответами на этой странице, проверьте этот jsperf (благодаря EaterOfCode). Реализация на основе цикла похожа по скорости на использование Array.push.apply, но имеет тенденцию быть немного медленнее, чем Array.slice.apply.

Интересно, что если добавляемый вами массив является разреженным, описанный выше метод на основе forEach может использовать преимущества разреженности и превосходить методы на основе .apply; проверьте этот jsperf, если вы хотите проверить это для себя.

Кстати, не поддавайтесь искушению (как я!) Дополнительно сократить реализацию forEach до:

Array.prototype.extend = function (array) {
    array.forEach(this.push, this);
}

потому что это приводит к результатам мусора! Почему? Поскольку Array.prototype.forEach предоставляет три аргумента вызываемой функции - это: (element_value, element_index, source_array). Все они будут помещены в ваш первый массив для каждой итерации forEach, если вы используете "forEach (this.push, this)"!

Ответ 4

Я чувствую себя самым элегантным в наши дни:

arr1.push(...arr2);

В статье статьи MDN о распространителе упоминается этот приятный славный способ в ES2015 (ES6):

Лучшее нажатие

Пример: push часто используется для перемещения массива в конец существующего массив. В ES5 это часто делается как:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Append all items from arr2 onto arr1
Array.prototype.push.apply(arr1, arr2);

В ES6 с расширением это становится:

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

Обратите внимание, что arr2 не может быть огромным (держите его под примерно 100 000 элементов), потому что стек вызовов переполняется, как ответ jcdude.

Ответ 5

Сначала несколько слов о apply() в JavaScript, чтобы понять, почему мы его используем:

Метод apply() вызывает функцию с заданным значением this, и аргументы представлены в виде массива.

Push ожидает список элементов для добавления в массив. Однако метод apply() принимает ожидаемые аргументы для вызова функции в виде массива. Это позволяет нам легко push элементы одного массива в другой массив с помощью встроенного метода push().

Представьте, что у вас есть эти массивы:

var a = [1, 2, 3, 4];
var b = [5, 6, 7];

и просто сделай это:

Array.prototype.push.apply(a, b);

Результат будет:

a = [1, 2, 3, 4, 5, 6, 7];

То же самое можно сделать в ES6 с помощью оператора распространения ("...") следующим образом:

a.push(...b); //a = [1, 2, 3, 4, 5, 6, 7]; 

Короче и лучше, но в настоящее время поддерживается не во всех браузерах.

Также, если вы хотите переместить все из массива b в a, опустошив b в процессе, вы можете сделать это:

while(b.length) {
  a.push(b.shift());
} 

и результат будет следующим:

a = [1, 2, 3, 4, 5, 6, 7];
b = [];

Ответ 6

Если вы хотите использовать jQuery, $. merge()

Пример:

a = [1, 2];
b = [3, 4, 5];
$.merge(a,b);

Результат: a = [1, 2, 3, 4, 5]

Ответ 7

Мне нравится метод a.push.apply(a, b), описанный выше, и если вы хотите, вы всегда можете создать библиотечную функцию следующим образом:

Array.prototype.append = function(array)
{
    this.push.apply(this, array)
}

и используйте его так:

a = [1,2]
b = [3,4]

a.append(b)

Ответ 8

Это можно сделать с помощью splice():

b.unshift(b.length)
b.unshift(a.length)
Array.prototype.splice.apply(a,b) 
b.shift() // Restore b
b.shift() // 

Но, несмотря на свою уродливость, он не быстрее, чем push.apply, по крайней мере, в Firefox 3.0.

Ответ 9

Это решение работает для меня (с использованием оператора распространения ECMAScript 6):

let array = ['my', 'solution', 'works'];
let newArray = [];
let newArray2 = [];
newArray.push(...array); // Adding to same array
newArray2.push([...array]); // Adding as child/leaf/sub-array
console.log(newArray);
console.log(newArray2);

Ответ 10

Я вижу очень хорошие ответы, и все зависит от размера вашего массива, от вашего требования и цели. Это можно сделать разными способами.

Я предлагаю использовать JavaScript для цикла:

var a = [1, 2];
var b = [3, 4, 5];

for (i = 0; i < b.length; i++) {
    a.push(b[i]);
}

console.log(a) будет

Array [ 1, 2, 3, 4, 5 ]

Затем вы можете сделать свою собственную функцию:

function extendArray(a, b){
    for (i = 0; i < b.length; i++) {
        a.push(b[i]);
    }
    return a;
}

console.log(extendArray(a, b)); вывод будет

Array [ 1, 2, 3, 4, 5 ]

Ответ 11

Вы можете создать polyfill для расширения, как показано ниже. Он добавит массив; на месте и вернуть себя, чтобы вы могли связать другие методы.

if (Array.prototype.extend === undefined) {
  Array.prototype.extend = function(other) {
    this.push.apply(this, arguments.length > 1 ? arguments : other);
    return this;
  };
}

function print() {
  document.body.innerHTML += [].map.call(arguments, function(item) {
    return typeof item === 'object' ? JSON.stringify(item) : item;
  }).join(' ') + '\n';
}
document.body.innerHTML = '';

var a = [1, 2, 3];
var b = [4, 5, 6];

print('Concat');
print('(1)', a.concat(b));
print('(2)', a.concat(b));
print('(3)', a.concat(4, 5, 6));

print('\nExtend');
print('(1)', a.extend(b));
print('(2)', a.extend(b));
print('(3)', a.extend(4, 5, 6));
body {
  font-family: monospace;
  white-space: pre;
}

Ответ 12

Объединяя ответы...

Array.prototype.extend = function(array) {
    if (array.length < 150000) {
        this.push.apply(this, array)
    } else {
        for (var i = 0, len = array.length; i < len; ++i) {
            this.push(array[i]);
        };
    }  
}

Ответ 13

Другое решение для объединения более двух массивов

var a = [1, 2],
    b = [3, 4, 5],
    c = [6, 7];

// Merge the contents of multiple arrays together into the first array
var mergeArrays = function() {
 var i, len = arguments.length;
 if (len > 1) {
  for (i = 1; i < len; i++) {
    arguments[0].push.apply(arguments[0], arguments[i]);
  }
 }
};

Затем вызовите и напечатайте как:

mergeArrays(a, b, c);
console.log(a)

Вывод будет: Array [1, 2, 3, 4, 5, 6, 7]

Ответ 14

Ответ очень прост.

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
(The following code will combine the two arrays.)

a = a.concat(b);

>>> a
[1, 2, 3, 4, 5]

Concat действует очень похоже на конкатенацию строк JavaScript. Он вернет комбинацию параметра, который вы поместили в функцию concat в конце массива, для которого вы вызываете функцию. Суть в том, что вы должны присвоить возвращаемое значение переменной, иначе оно будет потеряно. Так, например,

a.concat(b);  <--- This does absolutely nothing since it is just returning the combined arrays, but it does not do anything with it.

Ответ 15

Используйте Array.extend вместо Array.push для> 150 000 записей.

if (!Array.prototype.extend) {
  Array.prototype.extend = function(arr) {
    if (!Array.isArray(arr)) {
      return this;
    }

    for (let record of arr) {
      this.push(record);
    }

    return this;
  };
}

Ответ 16

Супер просто, не рассчитывать на операторов распространения или применять, если это проблема.

b.map(x => a.push(x));

После запуска некоторых тестов производительности это ужасно медленно, но отвечает на вопрос о том, чтобы не создавать новый массив. Concat работает значительно быстрее, даже jQuery $.merge() делает это.

https://jsperf.com/merge-arrays19b/1