Cómo extender una matriz JavaScript existente con otra matriz, sin crear una nueva matriz

Resuelto Dzinx asked hace 15 años • 20 respuestas

No parece haber una manera de extender una matriz JavaScript existente con otra matriz, es decir, emular el extendmétodo de Python.

Quiero lograr lo siguiente:

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

Sé que hay un a.concat(b)método, pero crea una nueva matriz en lugar de simplemente extender la primera. Me gustaría un algoritmo que funcione eficientemente cuando asea significativamente mayor que b(es decir, uno que no copie a).

Nota: Este no es un duplicado de ¿Cómo agregar algo a una matriz? -- el objetivo aquí es agregar todo el contenido de una matriz a la otra y hacerlo "en el lugar", es decir, sin copiar todos los elementos de la matriz extendida.

Dzinx avatar Sep 03 '09 22:09 Dzinx
Aceptado

El .pushmétodo puede tomar múltiples argumentos. Puede utilizar el operador de extensión para pasar todos los elementos de la segunda matriz como argumentos a .push:

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

Si su navegador no es compatible con ECMAScript 6, puede utilizar .applyen su lugar:

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

O quizás, si crees que está más claro:

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

Tenga en cuenta que todas estas soluciones fallarán con un error de desbordamiento de pila si la matriz bes demasiado larga (el problema comienza en aproximadamente 100.000 elementos, según el navegador).
Si no puede garantizar que bsea lo suficientemente corto, debe utilizar una técnica estándar basada en bucles que se describe en la otra respuesta.

Dzinx avatar Sep 03 '2009 15:09 Dzinx

Actualización 2018 : una mejor respuesta es una mía mása.push(...b) nueva :. Ya no votes a favor de este, ya que en realidad nunca respondió la pregunta, pero fue un truco de 2015 sobre el primer acceso a Google :)


Para aquellos que simplemente buscaron "extensión de matriz JavaScript" y llegaron aquí, pueden usar Array.concat.

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

Concat devolverá una copia de la nueva matriz, ya que el iniciador del hilo no quería. Pero es posible que no le importe (ciertamente, para la mayoría de los usos, esto estará bien).


También hay algo de azúcar ECMAScript 6 agradable para esto en forma de operador de extensión:

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

(También copia.)

odinho - Velmont avatar Jul 20 '2015 16:07 odinho - Velmont

Deberías utilizar una técnica basada en bucles. Otras respuestas en esta página que se basan en el uso .applypueden fallar en matrices grandes.

Una implementación basada en bucles bastante concisa es:

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

Luego puedes hacer lo siguiente:

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

La respuesta de DzinX (usando push.apply) y otros .applymétodos basados ​​fallan cuando la matriz que estamos agregando es grande (las pruebas muestran que para mí, grande es > 150.000 entradas aproximadamente en Chrome y > 500.000 entradas en Firefox). Puede ver que este error ocurre en este jsperf .

Se produce un error porque se excede el tamaño de la pila de llamadas cuando se llama a 'Function.prototype.apply' con una matriz grande como segundo argumento. (MDN tiene una nota sobre los peligros de exceder el tamaño de la pila de llamadas usando Function.prototype.apply ; consulte la sección titulada "aplicar y funciones integradas").

Para una comparación de velocidad con otras respuestas en esta página, consulte este jsperf (gracias a EaterOfCode). La implementación basada en bucles es similar en velocidad al uso de Array.push.apply, pero tiende a ser un poco más lenta que Array.slice.apply.

Curiosamente, si la matriz que está agregando es escasa, el forEachmétodo basado anterior puede aprovechar la escasez y superar a los .applymétodos basados; Echa un vistazo a este jsperf si quieres probarlo tú mismo.

Por cierto, no caigas en la tentación (¡como yo!) de acortar aún más la implementación de forEach a:

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

¡Porque esto produce resultados basura! ¿Por qué? Porque Array.prototype.forEachproporciona tres argumentos para la función que llama: estos son: (element_value, element_index, source_array). ¡Todos estos se insertarán en su primera matriz para cada iteración forEachsi usa "forEach(this.push, this)"!

jcdude avatar Jun 28 '2013 15:06 jcdude

Creo que lo más elegante estos días es:

arr1.push(...arr2);

El artículo de MDN sobre el operador de propagación menciona esta agradable forma azucarada en ES2015 (ES6):

Un mejor empujón

Ejemplo: push se utiliza a menudo para empujar una matriz hasta el final de una matriz existente. En ES5 esto suele hacerse como:

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

En ES6 con extensión esto se convierte en:

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

Tenga en cuenta que arr2no puede ser enorme (manténgalo por debajo de aproximadamente 100 000 elementos), porque la pila de llamadas se desborda, según la respuesta de jcdude.

odinho - Velmont avatar Feb 05 '2016 11:02 odinho - Velmont