Il modo più efficiente per creare un array JavaScript riempito a zero?

Qual è il modo più efficiente per creare una lunghezza arbitraria a zero riempito in JavaScript?

Anche se questo è un thread vecchio, volevo aggiungere i miei 2 centesimi. Non sei sicuro di quanto sia lento / veloce, ma è veloce. Ecco cosa faccio:

Se voglio pre-compilare con un numero:

 Array.apply(null, Array(5)).map(Number.prototype.valueOf,0); // [0, 0, 0, 0, 0] 

Se voglio precompilare con una stringa:

 Array.apply(null, Array(3)).map(String.prototype.valueOf,"hi") // ["hi", "hi", "hi"] 

Altre risposte hanno suggerito:

 new Array(5+1).join('0').split('') // ["0", "0", "0", "0", "0"] 

ma se vuoi 0 (il numero) e non “0” (zero all’interno di una stringa), puoi fare:

 new Array(5+1).join('0').split('').map(parseFloat) // [0, 0, 0, 0, 0] 

ES6 introduce Array.prototype.fill . Può essere usato in questo modo:

 new Array(len).fill(0); 

Non sono sicuro se è veloce, ma mi piace perché è breve e autodescrivente.

Non è ancora in IE ( verifica compatibilità ), ma è disponibile un polyfill .

Modo elegante per riempire un array con valori precalcolati

Ecco un altro modo per farlo usando ES6 che nessuno ha menzionato finora:

 > Array.from(Array(3), () => 0) < [0, 0, 0] 

Funziona passando una funzione mappa come secondo parametro di Array.from .

Nell'esempio sopra, il primo parametro alloca un array di 3 posizioni riempite con il valore undefined e quindi la funzione lambda esegue il mapping di ognuna di esse al valore 0 .

Sebbene Array(len).fill(0) sia più corto, non funziona se devi riempire l'array facendo prima un calcolo (so che la domanda non l'ha chiesto, ma molte persone finiscono qui cercando questo) .

Ad esempio, se hai bisogno di un array con 10 numeri casuali:

 > Array.from(Array(10), () => Math.floor(10 * Math.random())) < [3, 6, 8, 1, 9, 3, 0, 6, 7, 1] 

È più conciso (ed elegante) dell'equivalente:

 const numbers = Array(10); for (let i = 0; i < numbers.length; i++) { numbers[i] = Math.round(10 * Math.random()); } 

Questo metodo può anche essere utilizzato per generare sequenze di numeri sfruttando il parametro index fornito nel callback:

 > Array.from(Array(10), (d, i) => i) < [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

Risposta bonus: riempire un array usando String repeat()

Poiché questa risposta sta ricevendo una buona dose di attenzione, ho anche voluto mostrare questo fantastico trucco. Sebbene non sia così utile come la mia risposta principale, introdurrò il metodo String repeat() ancora poco conosciuto, ma molto utile. Ecco il trucco:

 > "?".repeat(10).split("").map(() => Math.floor(10 * Math.random())) < [5, 6, 3, 5, 0, 8, 2, 7, 4, 1] 

Splendido, no? repeat() è un metodo molto utile per creare una stringa che è la ripetizione della stringa originale un certo numero di volte. Dopodiché, split() crea una matrice per noi, che è quindi map() ped ai valori che vogliamo. Abbattendolo a pochi passi:

 > "?".repeat(10) < "??????????" > "?".repeat(10).split("") < ["?", "?", "?", "?", "?", "?", "?", "?", "?", "?"] > "?".repeat(10).split("").map(() => Math.floor(10 * Math.random())) < [5, 6, 3, 5, 0, 8, 2, 7, 4, 1] 

Il già citato metodo di riempimento ES 6 si occupa di questo bene. I browser desktop più moderni supportano già i metodi di prototipo di array richiesti a partire da oggi (Chromium, FF, Edge e Safari) [ 1 ]. Puoi cercare i dettagli su MDN . Un semplice esempio di utilizzo è

 a = new Array(10).fill(0); 

Dato l’attuale supporto del browser, dovresti essere cauto nell’usarlo, a meno che tu non sia sicuro che il pubblico utilizzi i moderni browser desktop.

Nota aggiunta agosto 2013, aggiornata a febbraio 2015: la risposta riportata di seguito dal 2009 si riferisce al tipo di Array generico di JavaScript. Non si riferisce ai nuovi array digitati definiti in ES2015 [e ora disponibili in molti browser], come Int32Array e così via. Si noti inoltre che ES2015 aggiunge un metodo di fill sia a matrici che a matrici tipizzate , che è probabilmente il modo più efficiente per riempirle …

Inoltre, può fare una grande differenza per alcune implementazioni come si crea la matrice. Il motore V8 di Chrome, in particolare, tenta di utilizzare un array ad alta efficienza e memoria contigua se lo ritiene ansible, spostandosi sull’array basato su oggetti solo quando necessario.


Con la maggior parte delle lingue, sarebbe pre-allocare, quindi riempire a zero, come questo:

 function newFilledArray(len, val) { var rv = new Array(len); while (--len >= 0) { rv[len] = val; } return rv; } 

Ma gli array JavaScript non sono realmente matrici , sono mappe chiave / valore come tutti gli altri oggetti JavaScript, quindi non c’è alcun “pre-allocazione” da fare (l’impostazione della lunghezza non assegna molti spazi per riempire), né C’è motivo di ritenere che il vantaggio del conto alla rovescia fino a zero (che è giusto per rendere il paragone nel loop veloce) non sia superato con l’aggiunta delle chiavi in ​​ordine inverso quando l’implementazione potrebbe aver ottimizzato la gestione delle chiavi in relazione agli array sulla teoria, generalmente li farai in ordine.

In effetti, Matthew Crumley ha sottolineato che il conto alla rovescia è decisamente più lento su Firefox rispetto al conteggio, un risultato che posso confermare – è la parte dell’array di esso (il looping a zero è ancora più veloce del looping fino a un limite in una var). Apparentemente aggiungere gli elementi alla matrice in ordine inverso è un’operazione lenta su Firefox. In realtà, i risultati variano un po ‘dall’implementazione di JavaScript (che non è poi così sorprendente). Ecco una pagina di test rapida e sporca (sotto) per le implementazioni del browser (molto sporca, non produce risultati durante i test, quindi fornisce un feedback minimo e si scontrerà con i limiti di tempo dello script). Raccomando di rinfrescare tra i test; FF (almeno) rallenta i test ripetuti se non lo fai.

La versione abbastanza complicata che utilizza l’array # concat è più veloce di un init straight su FF, da qualche parte tra 1.000 e 2.000 array di elementi. Sul motore V8 di Chrome, però, straight init vince ogni volta …

Ecco la pagina di test ( copia live ):

     Zero Init Test Page     



Per impostazione predefinita, le classi Uint8Array , Uint16Array e Uint32Array mantengono gli zeri come valori, quindi non sono necessarie tecniche di riempimento complesse, basta fare:

 var ary = new Uint8Array(10); 

per impostazione predefinita tutti gli elementi dell’array saranno zero.

 function makeArrayOf(value, length) { var arr = [], i = length; while (i--) { arr[i] = value; } return arr; } makeArrayOf(0, 5); // [0, 0, 0, 0, 0] makeArrayOf('x', 3); // ['x', 'x', 'x'] 

Nota che, while solito è più efficiente di for-in , forEach , ecc.

Ho testato tutte le combinazioni di pre-allocazione / non pre-allocazione, conteggio su / giù e per / while loop in IE 6/7/8, Firefox 3.5, Chrome e Opera.

Le funzioni seguenti erano costantemente più veloci o estremamente vicine in Firefox, Chrome e IE8, e non molto più lente del più veloce in Opera e IE 6. È anche la più semplice e chiara a mio parere. Ho trovato diversi browser in cui la versione del ciclo while è leggermente più veloce, quindi lo includo anche come riferimento.

 function newFilledArray(length, val) { var array = []; for (var i = 0; i < length; i++) { array[i] = val; } return array; } 

o

 function newFilledArray(length, val) { var array = []; var i = 0; while (i < length) { array[i++] = val; } return array; } 

Se si utilizza ES6, è ansible utilizzare Array.from () in questo modo:

 Array.from({ length: 3 }, () => 0); //[0, 0, 0] 

Ha lo stesso risultato di

 Array.from({ length: 3 }).map(() => 0) //[0, 0, 0] 

Perché

 Array.from({ length: 3 }) //[undefined, undefined, undefined] 

usando la notazione dell’object

 var x = []; 

zero riempito? piace…

 var x = [0,0,0,0,0,0]; 

riempito con ‘indefinito’ …

 var x = new Array(7); 

notazione obj con zeri

 var x = []; for (var i = 0; i < 10; i++) x[i] = 0; 

Come nota a margine, se modifichi il prototipo di Array, entrambi

 var x = new Array(); 

e

 var y = []; 

avrà quelle modifiche prototipo

Ad ogni modo, non sarei eccessivamente preoccupato dell'efficienza o della velocità di questa operazione, ci sono molte altre cose che probabilmente farai che sono molto più dispendiose e costose di quelle che instanziaeranno una serie di zeri a lunghezza arbitraria.

 function zeroFilledArray(size) { return new Array(size + 1).join('0').split(''); } 

Se è necessario creare molti array a pieno carico di lunghezza diversa durante l’esecuzione del codice, il modo più veloce che ho trovato per ottenere questo è di creare una matrice zero una volta , usando uno dei metodi menzionati su questo argomento, di una lunghezza che sai non verrà mai superato e quindi suddividere quella matrice se necessario.

Ad esempio (utilizzando la funzione dalla risposta scelta sopra per inizializzare la matrice), creare una matrice riempita a zero di lunghezza maxLength , come variabile visibile al codice che necessita di zero array:

 var zero = newFilledArray(maxLength, 0); 

Ora suddividere questo array ogni volta che è necessario un array di lunghezza zero riempito richiesto Lunghezza massima :

 zero.slice(0, requiredLength); 

Stavo creando array riempiti a zero migliaia di volte durante l’esecuzione del mio codice, questo ha accelerato enormemente il processo.

Il modo in cui di solito lo faccio (ed è incredibilmente veloce) sta usando Uint8Array . Ad esempio, creando un vettore a riempimento zero di elementi 1M:

  var zeroFilled = [].slice.apply(new Uint8Array(1000000)) 

Sono un utente Linux e ho sempre lavorato per me, ma una volta che un amico che utilizzava un Mac aveva alcuni elementi diversi da zero. Pensavo che la sua macchina non funzionasse correttamente, ma ancora qui è il modo più sicuro che abbiamo trovato per risolverlo:

  var zeroFilled = [].slice.apply(new Uint8Array(new Array(1000000)) 

Modificato

Chrome 25.0.1364.160

  1. Frederik Gottlieb – 6.43
  2. Sam Barnum – 4,83
  3. Eli – 3,68
  4. Joshua 2.91
  5. Mathew Crumley – 2.67
  6. bduran – 2,55
  7. Allen Rice – 2.11
  8. kangax – 0,68
  9. Tj. Crowder – 0,67
  10. zertosh: ERRORE

Firefox 20.0

  1. Allen Rice – 1.85
  2. Giosuè – 1.82
  3. Mathew Crumley – 1.79
  4. bduran – 1,37
  5. Frederik Gottlieb – 0,67
  6. Sam Barnum – 0,63
  7. Eli – 0,59
  8. kagax: 0,13
  9. Tj. Crowder – 0,13
  10. zertosh: ERRORE

Manca il test più importante (almeno per me): quello di Node.js. Sospetto che si avvicini al punto di riferimento di Chrome.

Usando lodash o underscore

 _.range(0, length - 1, 0); 

Oppure se hai un array esistente e vuoi un array della stessa lunghezza

 array.map(_.constant(0)); 

Non ho niente contro:

 Array.apply(null, Array(5)).map(Number.prototype.valueOf,0); new Array(5+1).join('0').split('').map(parseFloat); 

suggerito da Zertosh, ma in una nuova estensione di array ES6 consente di farlo in modo nativo con il metodo fill . Ora IE edge, Chrome e FF lo supportano, ma controlla la tabella di compatibilità

new Array(3).fill(0) ti darà [0, 0, 0] . È ansible riempire l’array con qualsiasi valore come new Array(5).fill('abc') (anche oggetti e altri array).

Inoltre puoi modificare gli array precedenti con il riempimento:

 arr = [1, 2, 3, 4, 5, 6] arr.fill(9, 3, 5) # what to fill, start, end 

che ti dà: [1, 2, 3, 9, 9, 6]

A partire da ECMAScript2016 , esiste una scelta chiara per i grandi array.

Dal momento che questa risposta si presenta ancora nelle prime posizioni nelle ricerche su google, ecco una risposta per il 2017.

Ecco un jsbench attuale con alcune dozzine di metodi popolari, tra cui molti proposti finora su questa domanda. Se trovi un metodo migliore, aggiungi, forchetta e condividi.

Voglio sottolineare che non esiste un vero e più efficace modo di creare un array con zero zero di lunghezza arbitraria. È ansible ottimizzare la velocità, la chiarezza e la manutenibilità: può essere considerata la scelta più efficiente in base alle esigenze del progetto.

Quando si ottimizza la velocità, si desidera: creare l’array usando la syntax letterale; imposta la lunghezza, inizializza la variabile iterating e itera attraverso la matrice usando un ciclo while. Ecco un esempio.

 const arr = []; arr.length = 120000; let i = 0; while (i < 120000) { arr[i] = 0; i++; } 

Soluzione ES6:

 [...new Array(5)].map(x => 0); // [0, 0, 0, 0, 0] 

Che ne dici di new Array(51).join('0').split('') ?

Non ho visto questo metodo nelle risposte, quindi eccolo qui:

 "0".repeat( 200 ).split("").map( parseFloat ) 

Di conseguenza otterrete una matrice di valore zero di lunghezza 200:

 [ 0, 0, 0, 0, ... 0 ] 

Non sono sicuro delle prestazioni di questo codice, ma non dovrebbe essere un problema se lo si utilizza per array relativamente piccoli.

La mia funzione più veloce sarebbe:

 function newFilledArray(len, val) { var a = []; while(len--){ a.push(val); } return a; } var st = (new Date()).getTime(); newFilledArray(1000000, 0) console.log((new Date()).getTime() - st); // returned 63, 65, 62 milliseconds 

L’utilizzo del push nativo e del passaggio per aggiungere elementi all’array è molto più veloce (circa 10 volte) rispetto alla dichiarazione dell’ambito della matrice e al riferimento a ciascun elemento per impostarne il valore.

fyi: I tempi sono sempre più veloci con il primo ciclo, che è il conto alla rovescia, quando si esegue questo in firebug (estensione firefox).

 var a = []; var len = 1000000; var st = (new Date()).getTime(); while(len){ a.push(0); len -= 1; } console.log((new Date()).getTime() - st); // returned 863, 894, 875 milliseconds st = (new Date()).getTime(); len = 1000000; a = []; for(var i = 0; i < len; i++){ a.push(0); } console.log((new Date()).getTime() - st); // returned 1155, 1179, 1163 milliseconds 

Mi interessa sapere cosa ne pensa di TJ Crowder? 🙂

Stavo testando la grande risposta di TJ Crowder, e ho inventato un’unione ricorsiva basata sulla soluzione concat che supera qualsiasi dei suoi test in Chrome (non ho testato altri browser).

 function makeRec(len, acc) { if (acc == null) acc = []; if (len <= 1) return acc; var b = makeRec(len >> 1, [0]); b = b.concat(b); if (len & 1) b = b.concat([0]); return b; }, 

chiama il metodo con makeRec(29) .

Questa versione concat è molto più veloce nei miei test su Chrome (2013-03-21). Circa 200ms per 10.000.000 elementi contro 675 per init dritto.

 function filledArray(len, value) { if (len <= 0) return []; var result = [value]; while (result.length < len/2) { result = result.concat(result); } return result.concat(result.slice(0, len-result.length)); } 

Bonus: se vuoi riempire il tuo array con Stringhe, questo è un modo conciso per farlo (non abbastanza veloce come concat ):

 function filledArrayString(len, value) { return new Array(len+1).join(value).split(''); } 

Sapevo di avere questo proto da qualche parte 🙂

 Array.prototype.init = function(x,n) { if(typeof(n)=='undefined') { n = this.length; } while (n--) { this[n] = x; } return this; } var a = (new Array(5)).init(0); var b = [].init(0,4); 

Modifica: test

In risposta a Joshua e ad altri metodi, ho eseguito il mio benchmarking e sto vedendo risultati completamente diversi da quelli riportati.

Ecco cosa ho provato:

 //my original method Array.prototype.init = function(x,n) { if(typeof(n)=='undefined') { n = this.length; } while (n--) { this[n] = x; } return this; } //now using push which I had previously thought to be slower than direct assignment Array.prototype.init2 = function(x,n) { if(typeof(n)=='undefined') { n = this.length; } while (n--) { this.push(x); } return this; } //joshua's method function newFilledArray(len, val) { var a = []; while(len--){ a.push(val); } return a; } //test m1 and m2 with short arrays many times 10K * 10 var a = new Date(); for(var i=0; i<10000; i++) { var t1 = [].init(0,10); } var A = new Date(); var b = new Date(); for(var i=0; i<10000; i++) { var t2 = [].init2(0,10); } var B = new Date(); //test m1 and m2 with long array created once 100K var c = new Date(); var t3 = [].init(0,100000); var C = new Date(); var d = new Date(); var t4 = [].init2(0,100000); var D = new Date(); //test m3 with short array many times 10K * 10 var e = new Date(); for(var i=0; i<10000; i++) { var t5 = newFilledArray(10,0); } var E = new Date(); //test m3 with long array created once 100K var f = new Date(); var t6 = newFilledArray(100000, 0) var F = new Date(); 

risultati:

 IE7 deltas: dA=156 dB=359 dC=125 dD=375 dE=468 dF=412 FF3.5 deltas: dA=6 dB=13 dC=63 dD=8 dE=12 dF=8 

Quindi la mia resa dei conti è in effetti più lenta in generale, ma funziona meglio con gli array più lunghi in FF, ma peggio in IE che fa schifo in generale (che sorpresa).

It might be worth pointing out, that Array.prototype.fill had been added as part of the ECMAScript 6 (Harmony) proposal . I would rather go with the polyfill written below, before considering other options mentioned on the thread.

 if (!Array.prototype.fill) { Array.prototype.fill = function(value) { // Steps 1-2. if (this == null) { throw new TypeError('this is null or not defined'); } var O = Object(this); // Steps 3-5. var len = O.length >>> 0; // Steps 6-7. var start = arguments[1]; var relativeStart = start >> 0; // Step 8. var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); // Steps 9-10. var end = arguments[2]; var relativeEnd = end === undefined ? len : end >> 0; // Step 11. var final = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); // Step 12. while (k < final) { O[k] = value; k++; } // Step 13. return O; }; } 
 var str = "0000000...0000"; var arr = str.split(""); 

usage in expressions: arr[i]*1;

EDIT: if arr supposed to be used in integer expressions, then please don’t mind the char value of ‘0’. You just use it as follows: a = a * arr[i] (assuming a has integer value).

Shortest for loop code

 a=i=[];for(;i<100;)a[i++]=0; edit: for(a=i=[];i<100;)a[i++]=0; or for(a=[],i=100;i--;)a[i]=0; 

Safe var version

 var a=[],i=0;for(;i<100;)a[i++]=0; edit: for(var i=100,a=[];i--;)a[i]=0; 

The fastest way to do that is with forEach =)

(we keep backward compatibility for IE < 9)

 var fillArray = Array.prototype.forEach ? function(arr, n) { arr.forEach(function(_, index) { arr[index] = n; }); return arr; } : function(arr, n) { var len = arr.length; arr.length = 0; while(len--) arr.push(n); return arr; }; // test fillArray([1,2,3], 'X'); // => ['X', 'X', 'X'] 

There’s always the phpjs solution, which you can find here:

http://phpjs.org/functions/array_fill/

I can’t speak for the project (creating a library of javascript functions that mirrors the greater functionality of php) as a whole, but the few functions that I’ve personally pulled from there have worked like a champ.

I just use :

 var arr = [10]; for (var i=0; i<=arr.length;arr[i] = i, i++); 

Anonymous function:

 (function(n) { while(n-- && this.push(0)); return this; }).call([], 5); // => [0, 0, 0, 0, 0] 

A bit shorter with for-loop:

 (function(n) { for(;n--;this.push(0)); return this; }).call([], 5); // => [0, 0, 0, 0, 0] 

Works with any Object , just change what’s inside this.push() .

You can even save the function:

 function fill(size, content) { for(;size--;this.push(content)); return this; } 

Call it using:

 var helloArray = fill.call([], 5, 'hello'); // => ['hello', 'hello', 'hello', 'hello', 'hello'] 

Adding elements to an already existing array:

 var helloWorldArray = fill.call(helloArray, 5, 'world'); // => ['hello', 'hello', 'hello', 'hello', 'hello', 'world', 'world', 'world', 'world', 'world'] 

Performance: http://jsperf.com/zero-filled-array-creation/25