pila - uso array javascript




Spingere gli elementi dell'array in un altro array (10)

Ho un array di dataArray JavaScript che voglio inserire in un nuovo array newArray . Tranne che non voglio che newArray[0] sia dataArray . Voglio inserire tutti gli elementi nel nuovo array:

var newArray = [];

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

o ancora 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. C'è qualche abbreviazione come pushValues disponibile quindi non devo dataArray su ogni singolo dataArray , aggiungendo gli elementi uno per uno?


Ricerca e risultati

Per i fatti, vengono eseguiti 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 basso.

╔═══════════════╦══════╦═════════════════╦═══════════════╦═════════╦══════════╗
║ Method        ║Concat║slice&push.apply ║ push.apply x2 ║ ForLoop ║Spread    ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║ mOps/Sec      ║179   ║104              ║ 76            ║ 81      ║28        ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║ Sparse arrays ║YES!  ║Only the sliced  ║ no            ║ Maybe2	 ║no        ║
║ kept sparse   ║      ║array (1st arg)  ║               ║         ║          ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║ Support       ║MSIE 4║MSIE 5.5         ║ MSIE 5.5      ║ MSIE 4  ║Edge 12   ║
║ (source)      ║NNav 4║NNav 4.06        ║ NNav 4.06     ║ NNav 3  ║MSIE NNav ║
╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣
║Array-like acts║no    ║Only the pushed  ║ YES!          ║ YES!    ║If have   ║
║like an array  ║      ║array (2nd arg)  ║               ║         ║iterator1	║
╚═══════════════╩══════╩═════════════════╩═══════════════╩═════════╩══════════╝
1 If the array-like object does not have a Symbol.iterator property, then trying
  to spread it will throw an exception.
2 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; i !== oneLen; ++i) {
        tmp = inputOne[i];
        if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;
    }
    for (var i=0, tmp; i !== oneLen; ++i) {
        tmp = inputOne[i];
        if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;
    }
    return newArr;
}

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

Il futuro

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

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

Come visto sopra, tutto quello 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 ogni indice nell'array e convertirlo in una serie di argomenti prima di passarlo a Array.prototype.push. Quindi, Array.prototype.push deve allocare sempre più memoria ogni volta e (per alcune implementazioni del browser) può anche ricalcolare alcuni dati di ricerca di posizione per la sparsità.

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

  1. Vai al negozio, acquista abbastanza carta necessaria per una copia di ciascun array sorgente. Quindi posizionare ciascuna pila di array di origine tramite una fotocopiatrice e pinzare insieme le due copie risultanti.
  2. Vai al negozio, acquista abbastanza carta per una singola copia del primo array sorgente. Quindi, copiare manualmente la matrice di origine sulla nuova carta, assicurandosi di riempire eventuali punti sparsi vuoti. Quindi, torna al negozio, acquista abbastanza carta per il secondo array sorgente. Quindi, passare attraverso il secondo array sorgente e copiarlo, assicurandosi che non vi siano vuoti vuoti nella copia. Quindi, unisci tutti i documenti copiati insieme.

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

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

chiarimenti

Array di riserva

Quando alcuni membri dell'array sono semplicemente mancanti. 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 gli array di riserva.

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

Array-Preferenze

Un array-like è un oggetto che ha almeno una proprietà length , ma non è stato inizializzato con la new Array o [] ; Ad esempio, gli oggetti sottostanti sono classificati come array.

{0: "foo", 1: "bar", length:2}
document.body.children
new Uint8Array(3)
  • Questo è array-like perché sebbene sia un (n) (tipizzato) array, costringerlo a un array cambia il costruttore.
(function(){return arguments})()

Osserva cosa succede usando un metodo che costringe gli array-like ad array come slice.

var slice = Array.prototype.slice;
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}() ));

  • NOTA: è una cattiva pratica analizzare l'argomento della funzione a causa delle prestazioni.

Osserva cosa succede usando un metodo che non costringe gli array-like agli array come concat.

var empty = [];
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}() ));


A condizione che i tuoi array non siano enormi (vedi l'avvertenza qui sotto), puoi usare il metodo push() dell'array a cui desideri aggiungere valori. push() può assumere più parametri in modo da poter utilizzare il suo metodo apply() per passare la matrice di valori da spingere come elenco di parametri di funzione. Questo ha il vantaggio rispetto all'utilizzo di concat() di aggiungere elementi alla matrice in luogo invece di creare una nuova matrice.

Tuttavia, sembra che per gli array di grandi dimensioni (dell'ordine di 100.000 membri o più), questo trucco possa fallire . Per tali array, l'utilizzo di un loop è un approccio migliore. Vedere https://.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 prototipo di Array :

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

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

... o emula il metodo push() originale consentendo più parametri usando 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, tra cui 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]);
    }
};

Aggiungerò ancora una risposta "a prova di futuro"

In ECMAScript 6, è possibile utilizzare l' operatore di diffusione :

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

L'operatore di diffusione non è ancora incluso in tutti i principali browser. Per la compatibilità corrente, vedere questa tabella di compatibilità (continuamente aggiornata).

Tuttavia, puoi utilizzare l'operatore di diffusione 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 di spread.


Ci sono un numero di risposte che parlano di MDN . 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 hai la 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]


Il seguente sembra il più semplice per me:

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

Poiché "push" richiede un numero variabile di argomenti, è possibile utilizzare il metodo apply della funzione push per spingere tutti gli elementi di un altro array. Costruisce una chiamata a push usando il suo primo argomento ("newArray" qui) come "this" e gli elementi dell'array come gli argomenti rimanenti.

La slice nella prima istruzione ottiene una copia del primo array, quindi non lo si modifica.

Aggiornamento Se si sta utilizzando una versione di javascript con slice disponibile, è possibile semplificare l'espressione push per:

newArray.push(...dataArray2)

La funzione seguente non ha un problema con la lunghezza degli array ed è migliore 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 i miei invii, quindi eccoli 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]);
    }

Premi Applica:

target.push.apply(target, source);

operatore di spread:

    target.push(...source);

e infine la 'lunghezza impostata e per il ciclo' è la funzione precedente


Questo è un codice funzionante e funziona bene:

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

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 utilizzare la funzione di spread operator di ES6:

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

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

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

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

Ecco il modo ES6

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







arrays