Copiar elementos de matriz en otra matriz

918

Tengo una matriz de JavaScript dataArrayque quiero insertar en una nueva matriz newArray. Excepto que no quiero newArray[0]serlo dataArray. Quiero insertar todos los elementos en la nueva matriz:

var newArray = [];

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

o mejor:

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

Entonces, la nueva matriz contiene todos los valores de las matrices de datos individuales. ¿Hay alguna abreviatura como pushValuesdisponible para que no tenga que iterar sobre cada individuo dataArray, agregando los elementos uno por uno?

bba
fuente
Esta debería ser la respuesta davidwalsh.name/combining-js-arrays
starikovs

Respuestas:

1249

Use la función concat , así:

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

El valor de newArrayserá [1, 2, 3, 4]( arrayAy arrayBpermanecerá sin cambios; concatcrea y devuelve una nueva matriz para el resultado).

WiseGuyEh
fuente
16
Estoy de acuerdo en que la ejecución performante es muy agradable. ¿PERO no es concat exactamente para ese propósito concat a arrays? Entonces debería ser estándar . ¿O hay otras cosas mejores que hacer con concat? ¿Y puede ser lento solo debido a una mala implementación del motor JS del navegador o donde sea que lo esté usando? Podría arreglarse algún día. Elegiría la mantenibilidad del código sobre las optimizaciones de velocidad hacky. Hmm ....
Bitterblue
3
También acabo de evaluar la situación: concat vs. push.apply. Google Chrome: rápido (concat = ganador) Opera,: rápido (concat = ganador) ,: IEmás lento (concat = ganador) Firefox,: lento (push.apply = ganador, pero 10 veces más lento que el concat de Chrome) ... habla de una mala implementación del motor JS .
Bitterblue
15
¿Cómo es la concatenación de dos matrices la respuesta aceptada de cómo empujar una a otra? Esas son dos operaciones diferentes.
kaqqao
@kaqqao porque pushno aplana una matriz de valores. concatlogra lo que la pregunta requiere.
WiseGuyEh
644

Siempre que sus matrices no sean enormes (vea la advertencia a continuación), puede usar el push()método de la matriz a la que desea agregar valores. push()puede tomar múltiples parámetros para que pueda usar su apply()método para pasar la matriz de valores que se empujará como una lista de parámetros de función. Esto tiene la ventaja sobre el uso concat()de agregar elementos a la matriz en su lugar en lugar de crear una nueva matriz.

Sin embargo, parece que para matrices grandes (del orden de 100,000 miembros o más), este truco puede fallar . Para tales matrices, usar un bucle es un mejor enfoque. Consulte https://stackoverflow.com/a/17368101/96100 para más detalles.

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

Es posible que desee generalizar esto en una función:

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

... o agregarlo al Arrayprototipo:

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

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

... o emule el push()método original al permitir múltiples parámetros utilizando el hecho de que concat(), al igual que push(), permite múltiples parámetros:

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

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

Aquí hay una versión del último ejemplo basada en bucles, adecuada para matrices grandes y todos los principales navegadores, incluido 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]);
    }
};
Tim Down
fuente
99
nota: newArray.push.apply(newArray, dataArray1);da lo mismo queArray.prototype.push.applay(newArray,dataArra1);
¿Es esto compatible con navegadores antiguos?
Damien Ó Ceallaigh
1
@ DamienÓCeallaigh: Sí. Todo esto es compatible con los navegadores que se remontan a IE 6 e incluso antes.
Tim Down
Este es el epítome de las malas prácticas de programación. Array.prototype.concatno es malo, sino que simplemente se malinterpreta y a veces se usa mal. En estas circunstancias, solo se entiende mal, pero no se usa mal.
Jack Giffin
446

Agregaré una respuesta más "a prueba de futuro"

En ECMAScript 6, puede usar la sintaxis Spread :

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

console.log(arr1)

La sintaxis extendida aún no se incluye en todos los principales navegadores. Para conocer la compatibilidad actual, consulte esta tabla de compatibilidad (actualizada continuamente) .

Sin embargo, puede usar la sintaxis de propagación con Babel.js .

editar:

Vea la respuesta de Jack Giffin a continuación para obtener más comentarios sobre el rendimiento. Parece que concat sigue siendo mejor y más rápido que el operador extendido.

Karel Bílek
fuente
77
También puede usar el operador de propagación si está usando TypeScript. Si apunta a ES5, se compilará a newArray.apply(newArray, dataArray1).
AJ Richardson
55
Nota: si necesita el resultado en una tercera matriz (no modificando arr1, como parecía requerir la pregunta inicial), puede hacer newArray = [... arr1, ... arr2]
dim
Oh no lo sabia. Gracias. He agregado un comentario de edición.
Karel Bílek
Similar a cómo funcionaría entonces concat, con la ventaja de ser persistente (mutar la matriz original).
Rob
@robertmylne El operador de propagación obviamente no modifica la matriz original, sino que crea una nueva copia de todos los contenidos de la matriz desaparecidos.
Jack Giffin
145

Encontrado una manera elegante de 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']

O puede usar la spread operatorfunción de ES6:

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

fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]
Creer2014
fuente
1
Esto no es lo que pedía la pregunta. La pregunta es pedir una forma de crear una nueva matriz cada vez, no modificar una matriz anterior.
Jack Giffin
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)

¿Eso resuelve tu problema?

Sébastien VINCENT
fuente
13

Lo siguiente me parece más simple:

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

Como "push" toma un número variable de argumentos, puede usar el applymétodo de la pushfunción para empujar todos los elementos de otra matriz. Construye una llamada para empujar usando su primer argumento ("newArray" aquí) como "this" y los elementos de la matriz como los argumentos restantes.

El sliceen la primera declaración obtiene una copia de la primera matriz, por lo que no la modifica.

Actualización Si está utilizando una versión de javascript con rebanada disponible, puede simplificar la pushexpresión para:

newArray.push(...dataArray2)
shaunc
fuente
1
También se menciona aquí en MDN como un ejemplo para "Combinar dos matrices"
Wilt
12

La siguiente función no tiene problemas con la longitud de las matrices y funciona mejor que todas las soluciones sugeridas:

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

desafortunadamente, jspref se niega a aceptar mis envíos, así que aquí están los resultados usando 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

dónde

para loop y push es:

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

Presione Aplicar:

target.push.apply(target, source);

operador de propagación:

    target.push(...source);

y finalmente el 'set length and for loop' es la función anterior

Panos Theof
fuente
Esta pregunta busca una manera de crear una nueva matriz cada vez, no modificar una matriz existente.
Jack Giffin
10

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

Para los hechos, se realiza una prueba de rendimiento en jsperf y se comprueban algunas cosas en la consola. Para la investigación, se utiliza el sitio web irt.org . A continuación se muestra una colección de todas estas fuentes juntas más una función de ejemplo en la parte inferior.

╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Método ║Concat║slice & push.apply ║ push.apply x2 ║ ForLoop ║Spread ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / Sec. ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Matrices dispersas ║SÍ! ║Solo las rebanadas ║ no ║ Quizás 2   ║no ║
║ mantenido escaso ║ ║ matriz (1er argumento) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Soporte ║MSIE 4║MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║Edge 12 ║
║ ( fuente ) ║NNav 4║NNav 4.06 ║ NNav 4.06 ║ NNav 3 ║ MSIE  NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║Actos de tipo matriz ║no ║Solo los empujados ║ ¡SÍ! ║ SI! ║Si tengo ║
║ como una matriz ║ ║ matriz (2º argumento) ║ ║ ║iterador 1   ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Si el objeto tipo matriz no tiene una propiedad Symbol.iterator , intente
  difundirlo arrojará una excepción.
2 Depende del código. El siguiente código de ejemplo "SÍ" conserva la escasez.
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;
}

Como se vio anteriormente, diría que Concat es casi siempre el camino a seguir para el rendimiento y la capacidad de retener la escasez de matrices de repuesto. Luego, para los me gusta de la matriz (como las listas DOMNodeLists document.body.children), recomendaría usar el bucle for porque es el segundo método más eficaz y el único otro método que retiene las matrices dispersas. A continuación, repasaremos rápidamente qué se entiende por matrices dispersas y me gusta de matrices para aclarar la confusión.

𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲

Al principio, algunas personas pueden pensar que esto es una casualidad y que los proveedores de navegadores eventualmente llegarán a optimizar Array.prototype.push para ser lo suficientemente rápido como para vencer a Array.prototype.concat. ¡INCORRECTO! Array.prototype.concat siempre será más rápido (al menos en principio) porque es un simple copiar y pegar sobre los datos. A continuación, se muestra un diagrama visual simplificado de cómo se vería una implementación de matriz de 32 bits (tenga en cuenta que las implementaciones reales son MUCHO más complicadas)

Byte ║ Datos aquí
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int longitud = 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 (o donde esté en la memoria)
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid

Como se ve arriba, todo lo que necesita hacer para copiar algo así es casi tan simple como copiarlo byte por byte. Con Array.prototype.push.apply, es mucho más que un simple copiar y pegar sobre los datos. El ".apply" tiene que verificar cada índice en la matriz y convertirlo en un conjunto de argumentos antes de pasarlo a Array.prototype.push. Luego, Array.prototype.push tiene que asignar adicionalmente más memoria cada vez, y (para algunas implementaciones de navegador) incluso puede recalcular algunos datos de búsqueda de posición por escasez.

Una forma alternativa de pensar es esto. La matriz de origen es una gran pila de papeles grapados entre sí. La matriz de origen dos también es otra gran pila de papeles. ¿Sería más rápido para ti

  1. Vaya a la tienda, compre suficiente papel para una copia de cada fuente. Luego, coloque cada pila de papel de la matriz de origen a través de una máquina copiadora y engrape las dos copias resultantes juntas.
  2. Vaya a la tienda, compre suficiente papel para una sola copia de la primera matriz fuente. Luego, copie la matriz de origen en el nuevo papel a mano, asegurándose de llenar cualquier espacio escaso en blanco. Luego, regrese a la tienda, compre suficiente papel para la segunda matriz fuente. Luego, revise la segunda matriz de origen y cópiela sin asegurarse de que no haya espacios en blanco en la copia. Luego, engrape todos los papeles copiados.

En la analogía anterior, la opción # 1 representa Array.prototype.concat mientras que # 2 representa Array.prototype.push.apply. Probemos esto con un JSperf similar que solo difiere en que este prueba los métodos sobre matrices dispersas, no sobre matrices sólidas. Uno puede encontrarlo aquí mismo .

Por lo tanto, descarto mi caso de que el futuro del rendimiento para este caso de uso en particular no radica en Array.prototype.push, sino en Array.prototype.concat.

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

𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀

Cuando ciertos miembros de la matriz simplemente faltan. Por ejemplo:

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

Alternativamente, javascript le permite inicializar matrices de repuesto fácilmente.

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

𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀

Un tipo de matriz es un objeto que tiene al menos una lengthpropiedad, pero no se inicializó con new Arrayo []; Por ejemplo, los siguientes objetos se clasifican como tipo matriz.

{0: "foo", 1: "bar", longitud: 2}
document.body.children
nuevo Uint8Array (3)
  • Esto es similar a una matriz porque, aunque es una matriz (n) (tipificada), coaccionarla a una matriz cambia el constructor.
(función () {argumentos de retorno}) ()

Observe lo que sucede usando un método que coacciona los me gusta de la matriz en matrices como el segmento.

  • NOTA: Es una mala práctica llamar a argumentos de corte en función debido al rendimiento.

Observe lo que sucede usando un método que no coacciona los me gusta de la matriz en matrices como concat.

Jack Giffin
fuente
9

Con JavaScript ES6, puede usar el operador ... como un operador de propagación que esencialmente convertirá la matriz en valores. Entonces, puedes hacer algo como esto:

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

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

Si bien la sintaxis es concisa, no sé cómo funciona internamente y cuáles son las implicaciones de rendimiento en grandes matrices.

Ryan H.
fuente
2
Si observa cómo Babel lo convierte , verá que no debería ser más lento que usar la Array.push.applytécnica.
emil.c
1
@JackGiffin Me refería a lo que Ryan mencionó que no sabe cómo funciona internamente y cuáles son las implicaciones de rendimiento, en realidad no estaba sugiriendo este enfoque. En cualquier caso, has hecho un muy buen trabajo en tu respuesta, una buena investigación, siempre es bueno conocer esos detalles.
emil.c
9

Aquí está la forma ES6

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

El método anterior es bueno para la mayoría de los casos y los casos que no lo son, tenga en cuenta concat, como si tuviera cientos de miles de elementos en matrices.

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

Mamba negro
fuente
8

Hay varias respuestas que hablan sobre Array.prototype.push.apply . Aquí hay un claro ejemplo:

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]

Si tiene sintaxis 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]

Stephen Quan
fuente
4

Si desea modificar la matriz original, puede extender y presionar:

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

Si desea asegurarse de que solo los elementos del mismo tipo entren en la sourcematriz (sin mezclar números y cadenas, por ejemplo), use 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);
}
orad
fuente
2

Tenemos dos matrices a y b. el código que hizo aquí es un valor de matriz se inserta en la matriz 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' ]
KARTHIKEYAN.A
fuente
1
Por favor, no solo publique el código como respuesta. Explica qué hace el código y cómo resuelve el problema.
Patrick Hund
0

Prueba esto:

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

en lugar de la función push (), use la función concat para IE. ejemplo,

var a=a.concat(a,new Array('amin'));
usuario1911703
fuente
1
ambos son muy compatibles con IE
Jack Giffin
-3

Este es un código de trabajo y funciona bien:

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))}
user4354031
fuente