Qual è il modo più efficiente per creare un array riempito zero di lunghezza arbitraria in JavaScript?
let i = 0; Array.from(Array(10), ()=>i++);
Qual è il modo più efficiente per creare un array riempito zero di lunghezza arbitraria in JavaScript?
let i = 0; Array.from(Array(10), ()=>i++);
Risposte:
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 auto-descrittivo.
Non è ancora in IE ( verifica la compatibilità ), ma è disponibile un polyfill .
new Array(len)
è dolorosamente lento. (arr = []).length = len; arr.fill(0);
è la soluzione più veloce che abbia mai visto ... o almeno legata
arr = Array(n)
e si (arr = []).length = n
comportano in modo identico in base alle specifiche. In alcune implementazioni si potrebbe essere più veloci, ma non credo che ci sia una grande differenza.
(arr = []).length = 1000;
contro arr = new Array(1000);
test di velocità in entrambi Chrome e FF ... l' new
è terribilmente lento. Ora, per lunghezze di array più piccole .. diciamo <50 o lì circa ... quindi new Array()
sembra funzionare meglio. Ma ..
arr.fill(0)
... tutto cambia. Ora, l'utilizzo new Array()
è più veloce nella maggior parte dei casi tranne quando si arriva a dimensioni di array> 100000 ... Quindi è possibile iniziare a vedere di nuovo aumentare la velocità. Ma se in realtà non devi pre-riempirlo con zeri e puoi usare la falisy standard di array vuoti. Quindi (arr = []).length = x
è follemente veloce nei miei casi di test per la maggior parte del tempo.
new Array(5).forEach(val => console.log('hi'));
vs new Array(5).fill(undefined).forEach(val => console.log('hi'));
.
Anche se questo è un vecchio thread, volevo aggiungere i miei 2 centesimi ad esso. Non sono sicuro di quanto sia lento / veloce, ma è uno di quelli veloci. Ecco cosa faccio:
Se voglio pre-riempire con un numero:
Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
// [0, 0, 0, 0, 0]
Se voglio pre-riempire 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]
Array.apply(null, new Array(5)).map(...)
? Perché semplicemente (nuova matrice (5)). Map (...) non funzionerà come dice la specifica
new
) Quando lo fai Array(5)
stai creando un oggetto che sembra un po 'così: { length: 5, __proto__: Array.prototype }
- prova console.dir( Array(5) )
. Si noti che non ha alcuna proprietà 0
, 1
, 2
, ecc Ma quando si apply
che fino alla Array
costruzione, è come dire Array(undefined, undefined, undefined, undefined, undefined)
. E ottieni un oggetto che sembra un po ' { length: 5, 0: undefined, 1: undefined...}
. map
funziona sulle proprietà 0
, 1
ecc. ed è per questo che il tuo esempio non funziona, ma quando lo usi apply
lo fa.
.apply
è in realtà quello che vuoi this
che sia. Per questi scopi this
non importa - ci interessa davvero solo la "caratteristica" di diffusione dei parametri di .apply
- quindi può avere qualsiasi valore. Mi piace null
perché è economico, probabilmente non vuoi usarlo {}
o []
dal momento che potresti creare un'istanza di un oggetto senza motivo.
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 della 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 associa ognuna di esse al valore0
.
Sebbene Array(len).fill(0)
sia più breve, non funziona se è necessario riempire l'array eseguendo prima alcuni calcoli (so che la domanda non l'ha fatta, ma molte persone finiscono qui per cercarlo) .
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]
repeat()
Dato che questa risposta sta suscitando molta attenzione, volevo anche mostrare questo fantastico trucco. Sebbene non sia utile come la mia risposta principale, introdurrà il repeat()
metodo String ancora poco noto, 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 un array per noi, che è quindi map()
ped ai valori desiderati. Suddividendolo in passaggi:
> "?".repeat(10)
< "??????????"
> "?".repeat(10).split("")
< ["?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]
> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]
repeat
trucco non è assolutamente voluto in produzione, Array.from()
va benissimo :-)
Soluzione più veloce
let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;
Soluzione più breve (maneggevole) (3 volte più lenta per piccoli array, leggermente più lenta per grande (la più lenta su Firefox))
Array(n).fill(0)
Oggi 2020.06.09 eseguo test su macOS High Sierra 10.13.6 su browser Chrome 83.0, Firefox 77.0 e Safari 13.1. Ho testato le soluzioni scelte per due casi di test
new Array(n)+for
(N) è la soluzione più veloce per piccoli array e grandi array (tranne Chrome, ma comunque molto veloce lì) ed è consigliata come soluzione cross-browser velocenew Float32Array(n)
(I) restituisce array non tipici (ad es. non è possibile chiamarepush(..)
), quindi non confronto i suoi risultati con altre soluzioni - tuttavia questa soluzione è circa 10-20x più veloce di altre soluzioni per array di grandi dimensioni su tutti i browserfor
(L, M, N, O) sono veloci per array di piccole dimensionifill
(B, C) sono veloci su Chrome e Safari ma sorprendentemente più lente su Firefox per array di grandi dimensioni. Sono di media velocità per piccoli arrayArray.apply
(P) genera un errore per array di grandi dimensioni
Il codice seguente presenta le soluzioni utilizzate nelle misure
Risultati di esempio per Chrome
let a=[]; for(i=n;i--;) a.push(0);
- ma è 4x più lento di fill(0)
- quindi non aggiornerò nemmeno l'immagine per quel caso.
Il già citato metodo di riempimento ES 6 si occupa bene di questo. La maggior parte dei browser desktop moderni supporta già oggi i metodi prototipo di array richiesti (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 tuo pubblico utilizzi i moderni browser desktop.
a = Array(10).fill(null).map(() => { return []; });
a = Array(10).fill(0).map( _ => [] );
Nota aggiunta ad agosto 2013, aggiornata a febbraio 2015: la risposta di seguito dal 2009 si riferisce al Array
tipo generico di JavaScript . Non si riferisce alle matrici tipizzate più recenti definite in ES2015 [e disponibili ora in molti browser], simili Int32Array
e simili. Si noti inoltre che ES2015 aggiunge un fill
metodo sia agli array che agli array tipizzati , che è probabilmente il modo più efficiente per riempirli ...
Inoltre, può fare una grande differenza per alcune implementazioni su come creare l'array. Il motore V8 di Chrome, in particolare, tenta di utilizzare un array di memoria contigua altamente efficiente se lo ritiene possibile, passando all'array basato su oggetti solo quando necessario.
Con la maggior parte delle lingue, sarebbe pre-allocato, quindi zero-fill, in questo modo:
function newFilledArray(len, val) {
var rv = new Array(len);
while (--len >= 0) {
rv[len] = val;
}
return rv;
}
Ma gli array JavaScript non sono realmente array , sono mappe chiave / valore proprio come tutti gli altri oggetti JavaScript, quindi non c'è "pre-allocazione" da fare (l'impostazione della lunghezza non alloca tanti spazi da riempire), né c'è qualche motivo per credere che il vantaggio di contare fino a zero (che è solo quello di rendere veloce il confronto nel ciclo) non sia superato aggiungendo le chiavi in ordine inverso quando l'implementazione potrebbe aver ottimizzato la loro 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 è notevolmente più lento su Firefox rispetto al conto alla rovescia, 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 un var). Apparentemente l'aggiunta di elementi all'array in ordine inverso è un'operazione lenta su Firefox. In effetti, i risultati variano abbastanza per l'implementazione JavaScript (che non è poi così sorprendente). Ecco una pagina di test veloce e sporca (sotto) per le implementazioni del browser (molto sporca, non cede durante i test, quindi fornisce un feedback minimo e si scontrerà con i limiti di tempo dello script). Consiglio di rinfrescare tra i test; FF (almeno) rallenta su test ripetuti se non lo fai.
La versione abbastanza complicata che utilizza Array # concat è più veloce di un init semplice su FF da qualche parte tra 1.000 e 2.000 array di elementi. Sul motore V8 di Chrome, tuttavia, l'iniz dritto vince ogni volta ...
Ecco la pagina di prova ( copia live ):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Zero Init Test Page</title>
<style type='text/css'>
body {
font-family: sans-serif;
}
#log p {
margin: 0;
padding: 0;
}
.error {
color: red;
}
.winner {
color: green;
font-weight: bold;
}
</style>
<script type='text/javascript' src='prototype-1.6.0.3.js'></script>
<script type='text/javascript'>
var testdefs = {
'downpre': {
total: 0,
desc: "Count down, pre-decrement",
func: makeWithCountDownPre
},
'downpost': {
total: 0,
desc: "Count down, post-decrement",
func: makeWithCountDownPost
},
'up': {
total: 0,
desc: "Count up (normal)",
func: makeWithCountUp
},
'downandup': {
total: 0,
desc: "Count down (for loop) and up (for filling)",
func: makeWithCountDownArrayUp
},
'concat': {
total: 0,
desc: "Concat",
func: makeWithConcat
}
};
document.observe('dom:loaded', function() {
var markup, defname;
markup = "";
for (defname in testdefs) {
markup +=
"<div><input type='checkbox' id='chk_" + defname + "' checked>" +
"<label for='chk_" + defname + "'>" + testdefs[defname].desc + "</label></div>";
}
$('checkboxes').update(markup);
$('btnTest').observe('click', btnTestClick);
});
function epoch() {
return (new Date()).getTime();
}
function btnTestClick() {
// Clear log
$('log').update('Testing...');
// Show running
$('btnTest').disabled = true;
// Run after a pause while the browser updates display
btnTestClickPart2.defer();
}
function btnTestClickPart2() {
try {
runTests();
}
catch (e) {
log("Exception: " + e);
}
// Re-enable the button; we don't yheidl
$('btnTest').disabled = false;
}
function runTests() {
var start, time, counter, length, defname, def, results, a, invalid, lowest, s;
// Get loops and length
s = $F('txtLoops');
runcount = parseInt(s);
if (isNaN(runcount) || runcount <= 0) {
log("Invalid loops value '" + s + "'");
return;
}
s = $F('txtLength');
length = parseInt(s);
if (isNaN(length) || length <= 0) {
log("Invalid length value '" + s + "'");
return;
}
// Clear log
$('log').update('');
// Do it
for (counter = 0; counter <= runcount; ++counter) {
for (defname in testdefs) {
def = testdefs[defname];
if ($('chk_' + defname).checked) {
start = epoch();
a = def.func(length);
time = epoch() - start;
if (counter == 0) {
// Don't count (warm up), but do check the algorithm works
invalid = validateResult(a, length);
if (invalid) {
log("<span class='error'>FAILURE</span> with def " + defname + ": " + invalid);
return;
}
}
else {
// Count this one
log("#" + counter + ": " + def.desc + ": " + time + "ms");
def.total += time;
}
}
}
}
for (defname in testdefs) {
def = testdefs[defname];
if ($('chk_' + defname).checked) {
def.avg = def.total / runcount;
if (typeof lowest != 'number' || lowest > def.avg) {
lowest = def.avg;
}
}
}
results =
"<p>Results:" +
"<br>Length: " + length +
"<br>Loops: " + runcount +
"</p>";
for (defname in testdefs) {
def = testdefs[defname];
if ($('chk_' + defname).checked) {
results += "<p" + (lowest == def.avg ? " class='winner'" : "") + ">" + def.desc + ", average time: " + def.avg + "ms</p>";
}
}
results += "<hr>";
$('log').insert({top: results});
}
function validateResult(a, length) {
var n;
if (a.length != length) {
return "Length is wrong";
}
for (n = length - 1; n >= 0; --n) {
if (a[n] != 0) {
return "Index " + n + " is not zero";
}
}
return undefined;
}
function makeWithCountDownPre(len) {
var a;
a = new Array(len);
while (--len >= 0) {
a[len] = 0;
}
return a;
}
function makeWithCountDownPost(len) {
var a;
a = new Array(len);
while (len-- > 0) {
a[len] = 0;
}
return a;
}
function makeWithCountUp(len) {
var a, i;
a = new Array(len);
for (i = 0; i < len; ++i) {
a[i] = 0;
}
return a;
}
function makeWithCountDownArrayUp(len) {
var a, i;
a = new Array(len);
i = 0;
while (--len >= 0) {
a[i++] = 0;
}
return a;
}
function makeWithConcat(len) {
var a, rem, currlen;
if (len == 0) {
return [];
}
a = [0];
currlen = 1;
while (currlen < len) {
rem = len - currlen;
if (rem < currlen) {
a = a.concat(a.slice(0, rem));
}
else {
a = a.concat(a);
}
currlen = a.length;
}
return a;
}
function log(msg) {
$('log').appendChild(new Element('p').update(msg));
}
</script>
</head>
<body><div>
<label for='txtLength'>Length:</label><input type='text' id='txtLength' value='10000'>
<br><label for='txtLoops'>Loops:</label><input type='text' id='txtLoops' value='10'>
<div id='checkboxes'></div>
<br><input type='button' id='btnTest' value='Test'>
<hr>
<div id='log'></div>
</div></body>
</html>
Per impostazione predefinita Uint8Array
, Uint16Array
e le Uint32Array
classi mantengono gli zeri come valori, quindi non sono necessarie tecniche di riempimento complesse, basta:
var ary = new Uint8Array(10);
tutti gli elementi dell'array ary
saranno zero per impostazione predefinita.
Array.isArray(ary)
è false
. La lunghezza è anche di sola lettura, quindi non è possibile inserire nuovi elementi come conary.push
0
il loro valore predefinito.
Array.from(new Uint8Array(10))
fornirà un array normale.
Array(n).fill(0)
a Chrome se ciò di cui hai veramente bisogno è un array JS. Se è possibile utilizzare un TypedArray, questo è molto più veloce di .fill(0)
, tuttavia, soprattutto se è possibile utilizzare il valore di inizializzatore predefinito di 0
. Non sembra esserci un costruttore che accetta un valore di riempimento e una lunghezza, come ha fatto C ++ std::vector
. Sembra che per qualsiasi valore diverso da zero sia necessario costruire un TypedArray azzerato e quindi riempirlo. : /
Se usi ES6, puoi usare 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]
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']
Si noti che while
di solito è più efficiente rispetto for-in
, forEach
e così via
i
variabile locale non è estranea? length
viene passato per valore, quindi dovresti essere in grado di diminuirlo direttamente.
arr[i] = value
). È molto più veloce scorrere dall'inizio alla fine e utilizzarlo arr.push(value)
. È fastidioso, perché preferisco il tuo metodo.
usando la notazione oggetto
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 si modifica il prototipo di Array, entrambi
var x = new Array();
e
var y = [];
avrà quelle modifiche prototipo
In ogni caso, non mi preoccuperei eccessivamente dell'efficienza o della velocità di questa operazione, ci sono molte altre cose che probabilmente farai che sono molto più dispendiose e costose dell'istituire una serie di lunghezza arbitraria contenente zeri.
null
s in questo array -var x = new Array(7);
new Array(7)
non non creare un array "pieno di indefinito". Crea un array vuoto con lunghezza 7.
(new Array(10)).fill(0)
.
Ho testato tutte le combinazioni di pre-allocazione / non pre-allocazione, conteggio su / giù e per / mentre i cicli in IE 6/7/8, Firefox 3.5, Chrome e Opera.
Le funzioni seguenti erano costantemente le più veloci o estremamente vicine in Firefox, Chrome e IE8, e non molto più lente delle più veloci in Opera e IE 6. È anche la più semplice e chiara secondo me. Ho trovato diversi browser in cui la versione del ciclo while è leggermente più veloce, quindi la 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;
}
var array = []
dichiarazione nella prima parte del ciclo for in realtà, separata solo da una virgola.
length
valore già assegnato in modo che non cambi continuamente. Ho portato una serie di 1 milione di zero da 40ms a 8 sulla mia macchina.
for (i = 0, array = []; i < length; ++i) array[i] = val;
.. Meno blocchi? ... comunque, anche ... se imposto la array.length
lunghezza del nuovo array alla lunghezza .. mi sembra di ottenere un altro aumento della velocità del 10% -15% in FF ... in Chrome, sembra raddoppiare la velocità -> var i, array = []; array.length = length; while(i < length) array[i++] = val;
(era ancora più veloce se l'ho lasciato come un for
loop ... ma l'init non è più necessario, quindi while
sembra essere più veloce in questa versione)
Se durante l'esecuzione del tuo codice devi creare molte matrici riempite di zero di diverse lunghezze, il modo più veloce che ho trovato per raggiungere questo obiettivo è creare una matrice zero una volta , usando uno dei metodi menzionati in questo argomento, di una lunghezza che sai non verrà mai superato, quindi suddividi quell'array secondo necessità.
Ad esempio (utilizzando la funzione dalla risposta scelta sopra per inizializzare l'array), creare un array a riempimento zero di lunghezza maxLength , come variabile visibile al codice che richiede zero array:
var zero = newFilledArray(maxLength, 0);
Ora suddividi questa matrice ogni volta che hai bisogno di una matrice riempita di zero di lunghezza richiesta. Lunghezza < lunghezza massima :
zero.slice(0, requiredLength);
Durante l'esecuzione del mio codice creavo migliaia di array riempiti zero, questo ha accelerato enormemente il processo.
Non ho nulla 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 un nuovo array ES6 le estensioni consentono di farlo nativamente con il fill
metodo. Ora IE edge, Chrome e FF lo supportano, ma controlla la tabella di compatibilità
new Array(3).fill(0)
ti darà [0, 0, 0]
. È possibile riempire l'array con qualsiasi valore come new Array(5).fill('abc')
(anche oggetti e altri array).
Inoltre puoi modificare le matrici precedenti con 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]
Il modo in cui lo faccio di solito (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 un amico che utilizzava un Mac aveva alcuni elementi diversi da zero. Pensavo che la sua macchina non funzionasse correttamente, ma ecco ancora il modo più sicuro che abbiamo trovato per risolverlo:
var zeroFilled = [].slice.apply(new Uint8Array(new Array(1000000))
Modificato
Chrome 25.0.1364.160
Firefox 20.0
Manca il test più importante (almeno per me): quello Node.js. Lo sospetto vicino al benchmark di Chrome.
Utilizzando lodash o trattino basso
_.range(0, length - 1, 0);
O se hai un array esistente e vuoi un array della stessa lunghezza
array.map(_.constant(0));
_.range(0, length, 0)
, credo. Lodash è esclusivo del valore finale
A partire da ECMAScript2016 , esiste una chiara scelta per array di grandi dimensioni.
Poiché questa risposta viene ancora visualizzata nella parte superiore delle ricerche su Google, ecco una risposta per il 2017.
Ecco un jsbench attuale con alcune dozzine di metodi popolari, inclusi molti proposti finora su questa domanda. Se trovi un metodo migliore, aggiungi, fork e condividi.
Voglio notare che non esiste un modo più efficace per creare un array riempito a zero di lunghezza arbitraria. È possibile ottimizzare la velocità, la chiarezza e la manutenibilità; entrambe possono essere considerate la scelta più efficiente a seconda delle esigenze del progetto.
Quando si ottimizza per la velocità, si desidera: creare l'array usando la sintassi letterale; imposta la lunghezza, inizializza la variabile iterante e itera attraverso l'array usando un ciclo while. Ecco un esempio
const arr = [];
arr.length = 120000;
let i = 0;
while (i < 120000) {
arr[i] = 0;
i++;
}
Un'altra possibile implementazione sarebbe:
(arr = []).length = n;
let i = 0;
while (i < n) {
arr[i] = 0;
i++;
}
Ma scoraggio fortemente l'uso pratico di questo secondo impianto in quanto è meno chiaro e non consente di mantenere l'ambito del blocco sulla variabile dell'array.
Questi sono significativamente più veloci rispetto al riempimento con un ciclo for e circa il 90% più veloci rispetto al metodo standard di
const arr = Array(n).fill(0);
Ma questo metodo di riempimento è ancora la scelta più efficiente per array più piccoli grazie alla sua chiarezza, concisione e manutenibilità. La differenza di prestazione probabilmente non ti ucciderà a meno che tu non stia realizzando molti array con lunghezze dell'ordine di migliaia o più.
Alcune altre note importanti. La maggior parte delle guide di stile consiglia di non utilizzare più var
senza un motivo molto speciale quando si utilizza ES6 o versioni successive. Utilizzare const
per variabili che non verranno ridefinite e let
per variabili che lo saranno. Il MDN e Style Guide di Airbnb sono luoghi ideali per andare per ulteriori informazioni sulle migliori pratiche. Le domande non riguardavano la sintassi, ma è importante che le persone nuove a JS conoscano questi nuovi standard quando cercano in queste risme di risposte vecchie e nuove.
Per creare un array tutto nuovo
new Array(arrayLength).fill(0);
Per aggiungere alcuni valori alla fine di un array esistente
[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]
//**To create an all new Array**
console.log(new Array(5).fill(0));
//**To add some values at the end of an existing Array**
let existingArray = [1,2,3]
console.log([...existingArray, ...new Array(5).fill(0)]);
Non ho visto questo metodo nelle risposte, quindi eccolo qui:
"0".repeat( 200 ).split("").map( parseFloat )
Di conseguenza otterrai una matrice a 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 usi per array relativamente piccoli.
const arr = Array.from({ length: 10 }).fill(0)
Questa concat
versione è molto più veloce nei miei test su Chrome (21-03-2013). Circa 200 ms per 10.000.000 di elementi contro 675 per init semplice.
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 di stringhe, questo è un modo conciso per farlo (non abbastanza veloce come concat
se):
function filledArrayString(len, value) {
return new Array(len+1).join(value).split('');
}
Stavo testando la grande risposta di TJ Crowder, e mi è venuta in mente un'unione ricorsiva basata sulla soluzione concat che supera qualsiasi dei suoi test su 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)
.
Potrebbe essere utile sottolineare che Array.prototype.fill
era stato aggiunto come parte della proposta ECMAScript 6 (Harmony) . Preferirei andare con il polyfill scritto di seguito, prima di considerare altre opzioni menzionate nel 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;
};
}
Più breve per il codice loop
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;
Versione var sicura
var a=[],i=0;for(;i<100;)a[i++]=0;
edit:
for(var i=100,a=[];i--;)a[i]=0;
n
, questa sarebbe più breve:for(var a=[];n--;a[n]=0);
let filled = [];
filled.length = 10;
filled.fill(0);
console.log(filled);
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'uso del push and shift nativo per aggiungere elementi all'array è molto più veloce (circa 10 volte) rispetto alla dichiarazione dell'ambito dell'array e al riferimento a ciascun elemento per impostarne il valore.
A proposito: ottengo costantemente tempi più veloci con il primo loop, che sta eseguendo il conto alla rovescia, quando eseguo 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
Sono interessato a sapere cosa ne fa TJ Crowder? :-)
while (len--)
.. ho preso i miei tempi di elaborazione da circa 60ms a circa 54ms
Sapevo di avere questo prototipo 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 altri metodi ho eseguito il mio benchmarking e sto vedendo risultati completamente diversi da quelli riportati.
Ecco cosa ho testato:
//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, a mio avviso, la spinta è in effetti più lenta in generale, ma funziona meglio con array più lunghi in FF ma peggio in IE che fa schifo in generale (quella sorpresa).
b = []...
) è del 10-15% più veloce del primo, ma è 10 volte più lento della risposta di Joshua.
else {this.length=n;}
dopo il this.length
segno di spunta. Ciò ridurrà un array già esistente, se necessario, quando init
lo reinizializzerà a una lunghezza diversa n
.
Funzione anonima:
(function(n) { while(n-- && this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]
Un po 'più corto con for-loop:
(function(n) { for(;n--;this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]
Funziona con qualsiasi Object
, basta cambiare quello che c'è dentro this.push()
.
Puoi persino salvare la funzione:
function fill(size, content) {
for(;size--;this.push(content));
return this;
}
Chiamalo usando:
var helloArray = fill.call([], 5, 'hello');
// => ['hello', 'hello', 'hello', 'hello', 'hello']
Aggiunta di elementi a un array già esistente:
var helloWorldArray = fill.call(helloArray, 5, 'world');
// => ['hello', 'hello', 'hello', 'hello', 'hello', 'world', 'world', 'world', 'world', 'world']
Prestazioni: http://jsperf.com/zero-filled-array-creation/25