Copia gli elementi dell'array in un altro array


918

Ho un array JavaScript dataArrayche voglio inserire in un nuovo array newArray. Solo che non voglio newArray[0]esserlo dataArray. Voglio inserire tutti gli elementi nel nuovo array:

var newArray = [];

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

o anche meglio:

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

Quindi ora il nuovo array contiene tutti i valori dei singoli array di dati. È disponibile una scorciatoia come quella, pushValuesquindi non devo iterare su ogni individuo dataArray, aggiungendo gli elementi uno per uno?



Questa dovrebbe essere la risposta davidwalsh.name/combining-js-arrays
starikovs

Risposte:


1249

Utilizzare la funzione concat , in questo modo:

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

Il valore di newArraysarà [1, 2, 3, 4]( arrayAe arrayBrimarrà invariato; concatcrea e restituisce un nuovo array per il risultato).



16
Concordo sul fatto che l'esecuzione degli artisti è molto bella. MA non è concaticare esattamente a tale scopo concaticare array? Quindi dovrebbe essere standard . O ci sono altre cose migliori da fare con concat? E potrebbe essere lento solo a causa della cattiva implementazione del motore JS del browser o ovunque tu lo stia utilizzando? Potrebbe essere risolto un giorno. Vorrei scegliere la manutenibilità del codice piuttosto che ottimizzazioni della velocità irregolari. Hmm ....
Bitterblue,

3
Inoltre ho appena analizzato la situazione: concat vs. push.apply. Google Chrome: veloce (concat = vincitore) ,: Operaveloce (concat = vincitore) ,: IEpiù lento (concat = vincitore) Firefox,: lento (push.apply = vincitore, ma 10 volte più lento del concat di Chrome) ... parla di cattiva implementazione del motore JS .
Bitterblue,

15
In che modo concatenare due matrici è la risposta accettata per come spingerne uno nell'altro ?! Quelle sono due diverse operazioni.
Kaqqao,

@kaqqao perché pushnon appiattisce una matrice di valori. concatraggiunge ciò che la domanda richiede.
WiseGuyEh

644

A condizione che le matrici non siano enormi (vedere l'avvertenza di seguito), è possibile utilizzare il push()metodo dell'array a cui si desidera aggiungere valori. push()può accettare più parametri in modo da poter utilizzare il suo apply()metodo per passare l'array di valori da inviare come elenco di parametri di funzione. Ciò ha il vantaggio di utilizzare l' concat()aggiunta di elementi all'array in atto piuttosto che la creazione di un nuovo array.

Tuttavia, sembra che per grandi array (dell'ordine di 100.000 membri o più), questo trucco può fallire . Per tali array, l'utilizzo di un loop è un approccio migliore. Vedi https://stackoverflow.com/a/17368101/96100 per i dettagli.

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

Potresti voler generalizzare questo in una funzione:

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

... o aggiungilo al Arrayprototipo di:

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

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

... o emulare il push()metodo originale consentendo più parametri utilizzando il fatto che concat(), come push(), consente più parametri:

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

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

Ecco una versione basata su loop dell'ultimo esempio, adatta per array di grandi dimensioni e tutti i principali browser, incluso 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
nota: newArray.push.apply(newArray, dataArray1);fornisce lo stesso diArray.prototype.push.applay(newArray,dataArra1);

È compatibile con i browser più vecchi?
Damien Ó Ceallaigh,


1
@ DamienÓCeallaigh: Sì. Tutto questo è compatibile con i browser che risalgono a IE 6 e anche prima.
Tim Down

Questo è l'epitome di cattive pratiche di programmazione. Array.prototype.concatnon è male, piuttosto è semplicemente frainteso e talvolta abusato. In queste circostanze, è solo frainteso, ma non abusato.
Jack Giffin,

446

Aggiungerò un'altra risposta "a prova di futuro"

In ECMAScript 6, è possibile utilizzare la sintassi Spread :

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

console.log(arr1)

La sintassi diffusa non è ancora inclusa in tutti i principali browser. Per la compatibilità corrente, vedere questa tabella di compatibilità (continuamente aggiornata) .

Tuttavia, puoi utilizzare la sintassi diffusa con Babel.js .

modificare:

Vedi la risposta di Jack Giffin di seguito per ulteriori commenti sulle prestazioni. Sembra che concat sia ancora migliore e più veloce dell'operatore spread.


7
È inoltre possibile utilizzare l'operatore di diffusione se si utilizza TypeScript. Se scegli come target ES5, verrà compilato newArray.apply(newArray, dataArray1).
AJ Richardson,

5
Nota: se hai bisogno del risultato in un terzo array (quindi non modificando arr1, poiché la domanda iniziale sembrava richiedere), puoi fare newArray = [... arr1, ... arr2]
dim

Oh non lo sapevo. Grazie. Ho aggiunto un commento di modifica.
Karel Bílek,

Simile a come funzionerebbe concat allora, con il vantaggio di essere persistente (mutando l'array originale).
Rob

@robertmylne L'operatore spread ovviamente non modifica l'array originale, creando invece una nuova copia di tutti i contenuti degli array inviati.
Jack Giffin,

145

Trovato un modo elegante da 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']

Oppure puoi usare la spread operatorfunzione di ES6:

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

fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]

1
Non è questa la domanda. La domanda è chiedere un modo per creare un nuovo array ogni volta, non modificare un vecchio array.
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)

Questo risolve il tuo problema?


13

Quanto segue mi sembra più semplice:

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

Poiché "push" accetta un numero variabile di argomenti, è possibile utilizzare il applymetodo della pushfunzione per inviare tutti gli elementi di un altro array. Costruisce una chiamata a push usando il suo primo argomento ("newArray" qui) come "questo" e gli elementi dell'array come gli argomenti rimanenti.

La sliceprima istruzione ottiene una copia del primo array, quindi non la modifichi.

Aggiorna Se stai utilizzando una versione di javascript con slice disponibile, puoi semplificare l' pushespressione per:

newArray.push(...dataArray2)

1
Viene anche menzionato qui alla MDN come esempio di "Unione di due array"
Wilt,

12

La funzione seguente non ha problemi con la lunghezza degli array e offre prestazioni migliori di tutte le soluzioni suggerite:

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

sfortunatamente, jspref rifiuta di accettare le mie proposte, quindi ecco i risultati 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

dove

per loop e push è:

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

Push Apply:

target.push.apply(target, source);

operatore di diffusione:

    target.push(...source);

e infine la 'set length and for loop' è la funzione sopra


Questa domanda è alla ricerca di un modo per creare un nuovo array ogni volta, non modificare un array esistente.
Jack Giffin,

10

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

Per i fatti, viene eseguito un test delle prestazioni su jsperf e il controllo di alcune cose nella console. Per la ricerca, viene utilizzato il sito Web irt.org . Di seguito una raccolta di tutte queste fonti messe insieme più una funzione di esempio in fondo.

╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Metodo oncConcetti║slice & push.apply ║ push.apply x2 ║ ForLoop ║Scover ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / sec ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Matrici sparse ║ SÌ! ║Solo affettato ║ no ║ Forse 2   ║no ║
║ tenuto scarso ║array (1 ° arg) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Supporto ║MSIE 4║MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║Edge 12 ║
║ ( fonte ) ║NNav 4║NNav 4.06 ║ NNav 4.06 ║ NNav 3 ║ MSIE  NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Atti simili ad array║no ║Solo la spinta ║ SÌ! ║ SÌ! FSe hai ║
║come un array ║ ║array (2nd arg) ║ ║ ║iteratore 1   ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Se l'oggetto simile a un array non ha una proprietà Symbol.iterator , provare
  diffonderlo genererà un'eccezione.
2 Dipende dal codice. Il seguente codice di esempio "SÌ" preserva la scarsità.
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;
}

Come visto sopra, direi che Concat è quasi sempre la strada da percorrere sia per le prestazioni che per la capacità di conservare la scarsità di array di riserva. Quindi, per gli array-like (come DOMNodeLists come document.body.children), consiglierei di usare il ciclo for perché è sia il secondo più performante sia l'unico altro metodo che conserva gli array sparsi. Di seguito, analizzeremo rapidamente cosa si intende per array sparsi e simili a array per chiarire la confusione.

𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲

Inizialmente, alcune persone potrebbero pensare che si tratti di un colpo di fortuna e che i venditori di browser alla fine si adopereranno per ottimizzare Array.prototype.push per essere abbastanza veloci da battere Array.prototype.concat. SBAGLIATO! Array.prototype.concat sarà sempre più veloce (almeno in linea di principio) perché è una semplice copia-incolla sui dati. Di seguito è riportato un diagramma persuado-visivo semplificato di come potrebbe apparire un'implementazione di array a 32 bit (si noti che le implementazioni reali sono MOLTO più complicate)

Byte ║ Dati qui
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem
0x00 ║ int length = 0x00000001
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem
0x00 ║ int valueIndex = 0x00000000
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (o ovunque sia in memoria)
0x01 ║ ibidem
0x02 ║ ibidem
0x03 ║ ibidem

Come visto sopra, tutto ciò che devi fare per copiare qualcosa del genere è quasi semplice come copiarlo byte per byte. Con Array.prototype.push.apply, è molto più di una semplice copia-incolla sui dati. ".Apply" deve controllare ciascun indice dell'array e convertirlo in una serie di argomenti prima di passarlo a Array.prototype.push. Quindi, Array.prototype.push deve allocare ulteriore memoria ogni volta e (per alcune implementazioni del browser) potrebbe persino ricalcolare alcuni dati di ricerca della posizione per scarsità.

Un modo alternativo di pensarci è questo. L'array di origine uno è una grande pila di fogli pinzati insieme. L'array di origine due è anche un'altra grande pila di fogli. Sarebbe più veloce per te

  1. Vai al negozio, acquista abbastanza carta necessaria per una copia di ciascun array di origine. Quindi inserire ciascuna matrice di carta pile di carta attraverso una fotocopiatrice e pinzare insieme le due copie risultanti.
  2. Vai al negozio, acquista abbastanza carta per una singola copia del primo array di origine. Quindi, copiare manualmente l'array di origine sul nuovo foglio, assicurandosi di riempire eventuali spazi vuoti. Quindi, torna al negozio, acquista abbastanza carta per il secondo array di origine. Quindi, passare attraverso il secondo array di origine e copiarlo senza garantire spazi vuoti nella copia. Quindi, pinzare insieme tutti i documenti copiati.

Nell'analogia sopra, l'opzione 1 rappresenta Array.prototype.concat mentre # 2 rappresenta Array.prototype.push.apply. Proviamo questo con un JSperf simile che differisce solo per il fatto che questo testa i metodi su array sparsi, non su array solidi. Lo si può trovare proprio qui .

Pertanto, appoggio il caso che il futuro delle prestazioni per questo particolare caso d'uso non risieda in Array.prototype.push, ma piuttosto in Array.prototype.concat.

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

𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀

Quando alcuni membri dell'array mancano semplicemente. Per esempio:

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

In alternativa, JavaScript consente di inizializzare facilmente array di riserva.

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

𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀

Un array-like è un oggetto che ha almeno una lengthproprietà, ma non è stato inizializzato con new Arrayo []; Ad esempio, gli oggetti seguenti sono classificati come array.

{0: "foo", 1: "bar", lunghezza: 2}
document.body.children
nuovo Uint8Array (3)
  • È simile ad un array perché, sebbene sia un array (n) (tipizzato), il suo inserimento in un array modifica il costruttore.
(function () {return argomenti}) ()

Osserva cosa succede usando un metodo che costringe i array-like in array come slice.

  • NOTA: è una cattiva pratica chiamare slice sugli argomenti della funzione a causa delle prestazioni.

Osservare ciò che accade utilizzando un metodo che non obbliga gli array-like in array come concat.


9

Con JavaScript ES6, è possibile utilizzare l'operatore ... come operatore di diffusione che essenzialmente convertirà l'array in valori. Quindi, puoi fare qualcosa del genere:

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

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

Mentre la sintassi è concisa, non so come funzioni internamente e quali siano le implicazioni delle prestazioni su array di grandi dimensioni.


2
Se dai un'occhiata a come Babel lo converte , noterai che non dovrebbe essere più lento dell'uso della Array.push.applytecnica.
emil.c,

1
@JackGiffin Mi riferivo solo a ciò che Ryan ha detto che non sa come funziona internamente e quali sono le implicazioni delle prestazioni, in realtà non stavo suggerendo questo approccio. In ogni caso, hai fatto un ottimo lavoro sulla tua risposta, bella ricerca, è sempre bene conoscere tali dettagli.
emil.c,

9

Ecco il modo ES6

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

Il metodo sopra è valido per la maggior parte dei casi e non è consigliabile concat, come nel caso di centinaia di migliaia di elementi negli array.

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


8

Esistono numerose risposte a proposito di Array.prototype.push.apply . Ecco un chiaro esempio:

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]

Se si dispone della sintassi 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

Se si desidera modificare l'array originale, è possibile diffondere e spingere:

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

Se vuoi assicurarti che solo gli elementi dello stesso tipo vadano sourcenell'array (non mescolando numeri e stringhe per esempio), usa 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

Abbiamo due array a e b. il codice che ha fatto qui è array un valore è inserito nell'array 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
Per favore, non solo inserire il codice come risposta. Spiega cosa fa il codice e come risolve il problema.
Patrick Hund,

0

Prova questo:

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

-2

invece della funzione push () usa la funzione concat per IE. esempio,

var a=a.concat(a,new Array('amin'));

1
entrambi sono molto compatibili con IE
Jack Giffin,

-3

Questo è un codice funzionante e funziona benissimo:

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))}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.