Il modo più efficiente per creare un array JavaScript con riempimento zero?


602

Qual è il modo più efficiente per creare un array riempito zero di lunghezza arbitraria in JavaScript?


7
Alcuni dati reali su questo: jsperf.com/zeroarrayjs
Web_Designer

7
Il riempimento ES6 consente di eseguire questa operazione in modo nativo.
Salvador Dali,

1
arr = new Array (lunghezza + 1) .joint (carattere) .split ('');
Jordan Stefanelli,

4
AGGIORNAMENTO 2016 : Un altro benchmark personalizzato qui: jsfiddle.net/basickarl/md5z0Lqq
K - La tossicità in SO sta crescendo.

1
let i = 0; Array.from(Array(10), ()=>i++);
Bart Hoekstra,

Risposte:


543

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 .


15
il riempimento è veloce. new Array(len)è dolorosamente lento. (arr = []).length = len; arr.fill(0);è la soluzione più veloce che abbia mai visto ... o almeno legata
Pimp Trizkit,

7
@PimpTrizkit arr = Array(n)e si (arr = []).length = ncomportano in modo identico in base alle specifiche. In alcune implementazioni si potrebbe essere più veloci, ma non credo che ci sia una grande differenza.
Oriol,

2
Bene, ho iniziato a testarlo con array multidimensionali e sembrava accelerare notevolmente i miei casi di test. Dopo aver appena fatto qualche altro test su FF41 e Chrome45.0.2454.99 m. Sì, credo di aver davvero bisogno di più spazio per spiegarmi. La maggior parte dei miei test è stata una propensione, lo ammetto. Ma dai un'occhiata. Predefinire un var e utilizzando solo questa linea (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 ..
Pimp Trizkit,

4
... Devo ammettere che ho perso questa parte ... quando aggiungo la seconda riga al test ... 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.
Pimp Trizkit,

4
Si noti che per scorrere sull'array (ad esempio map o forEach) è necessario impostare i valori , altrimenti salterà quegli indici. I valori impostati possono essere quelli che desideri, anche se non definiti. Esempio: try new Array(5).forEach(val => console.log('hi'));vs new Array(5).fill(undefined).forEach(val => console.log('hi'));.
ArneHugo,

387

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]

6
Bella risposta! Puoi per favore spiegare il trucco con Array.apply(null, new Array(5)).map(...)? Perché semplicemente (nuova matrice (5)). Map (...) non funzionerà come dice la specifica
Dmitry Pashkevich,

36
(a proposito, non abbiamo davvero bisogno del 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 applyche fino alla Arraycostruzione, è come dire Array(undefined, undefined, undefined, undefined, undefined). E ottieni un oggetto che sembra un po ' { length: 5, 0: undefined, 1: undefined...}. mapfunziona sulle proprietà 0, 1ecc. ed è per questo che il tuo esempio non funziona, ma quando lo usi applylo fa.
zertosh,

4
Il primo parametro per .applyè in realtà quello che vuoi thische sia. Per questi scopi thisnon importa - ci interessa davvero solo la "caratteristica" di diffusione dei parametri di .apply- quindi può avere qualsiasi valore. Mi piace nullperché è economico, probabilmente non vuoi usarlo {}o []dal momento che potresti creare un'istanza di un oggetto senza motivo.
zertosh,

2
Anche l'inizializzazione con dimensione + assegnazione è molto più veloce di push. Vedi il test case jsperf.com/zero-fill-2d-array
Colin,

2
che dire di Array.apply (null, Array (5)). map (x => 0)? È un po 'più corto!
Arch Linux Tux

97

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 della mappa come secondo parametro di Array.from .

Nell'esempio sopra, il primo parametro alloca un array di 3 posizioni riempite con il valore undefinede 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]

Risposta bonus: riempire un array usando String 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]

Un sacco di trucchi da salotto in quel post, ma speriamo che nessuno raggiunga il codice di produzione :)
Eric Grange

Anche se il repeattrucco non è assolutamente voluto in produzione, Array.from()va benissimo :-)
Lucio Paiva,

Non proprio, Array.from () qui sta fondamentalmente creando un array, iterando attraverso di esso con map (), chiamando una funzione su ciascun elemento per creare un nuovo array, quindi scartando il primo array ... Per un piccolo array questo potrebbe essere innocuo, per array più grandi, questo è il tipo di modello che porta le persone a chiamare i browser "maiali della memoria" :)
Eric Grange,

Le persone che si occupano di array di grandi dimensioni dovrebbero sicuramente conoscerne meglio. Per le app comuni, tuttavia, la creazione di un array aux di dimensioni regolari (fino a 10k elementi) che verrà immediatamente smaltito è perfettamente soddisfacente (richiede lo stesso tempo di se si evitasse la creazione di un array aggiuntivo, testato con l'ultimo Chrome). Per casi del genere, la leggibilità diventa più importante delle minuscole ottimizzazioni delle prestazioni. Circa il tempo O (n), è necessario se devi calcolare qualcosa di diverso per ogni elemento (l'argomento principale della mia risposta). Questa discussione è molto interessante, felice di averla sollevata!
Lucio Paiva,

88

In breve

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)


Dettagli

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

  • piccolo array - con 10 elementi - è possibile eseguire test QUI
  • array di grandi dimensioni - con elementi 1M - è possibile eseguire il test QUI

conclusioni

  • soluzione basata su 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 veloce
  • la soluzione basata su new 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 browser
  • soluzioni basate su for (L, M, N, O) sono veloci per array di piccole dimensioni
  • soluzioni basate su fill (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 array
  • la soluzione basata su Array.apply(P) genera un errore per array di grandi dimensioni

inserisci qui la descrizione dell'immagine

Codice ed esempio

Il codice seguente presenta le soluzioni utilizzate nelle misure

Risultati di esempio per Chrome

inserisci qui la descrizione dell'immagine


Ho appena eseguito alcuni test su Chrome 77 e un semplice ciclo con push () è due volte più veloce di fill () ... Mi chiedo quali sottili effetti collaterali di fill () impediscano un'implementazione più efficiente?
Eric Grange,

@EricGrange Io aggiorno la risposta - in fondo aggiorno il link a benchamrk con la tua proposta: caso P 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.
Kamil Kiełczewski,

2
Belle misure. Analisi: G è lento a causa del ridimensionamento dell'array ad ogni iterazione e ridimensionare significa fare una nuova allocazione di memoria. A, B, M veloce perché il dimensionamento viene eseguito una sola volta. +1
Roland

63

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.


4
Se si riempie con un tipo di riferimento, sarà lo stesso riferimento in tutti. new Array (10) .fill (null) .map (() => []) sarebbe un modo sintetico per aggirare questo problema (mi ha bruciato inizialmente haha)
John Culviner

4
AGGIORNAMENTO 2016 : questo metodo spazza via tutto il resto dall'acqua, fare clic qui per i benchmark: jsfiddle.net/basickarl/md5z0Lqq
K - La tossicità in SO sta crescendo.

questo funzionerà per gli array. a = Array(10).fill(null).map(() => { return []; });
Andy,

2
@AndrewAnthonyGerst Terser:a = Array(10).fill(0).map( _ => [] );
Phrogz,

50

Nota aggiunta ad agosto 2013, aggiornata a febbraio 2015: la risposta di seguito dal 2009 si riferisce al Arraytipo generico di JavaScript . Non si riferisce alle matrici tipizzate più recenti definite in ES2015 [e disponibili ora in molti browser], simili Int32Arraye simili. Si noti inoltre che ES2015 aggiunge un fillmetodo 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>

Non sei sicuro che il riempimento all'indietro sia importante qui, dato che stai solo accedendo agli elementi (non eliminandoli) e hai già pre-allocato. Ho sbagliato?
Trittico,

il punto del riempimento all'indietro non ha particolare a che fare con l'array, ma per il momento ha a che fare con la condizione di escape - il falso 0 termina il ciclo in modo molto efficiente
annakata

(anche se ho appena notato che questo codice non ne fa uso)
annakata,

@annakata, non puoi usarlo qui, perché 0 è un indice valido.
Trittico,

@triptych: non è vero, tutto ciò che serve è il giusto ordine - vedi il mio post
annakata

34

Per impostazione predefinita Uint8Array, Uint16Arraye le Uint32Arrayclassi mantengono gli zeri come valori, quindi non sono necessarie tecniche di riempimento complesse, basta:

var ary = new Uint8Array(10);

tutti gli elementi dell'array arysaranno zero per impostazione predefinita.


5
Questo è bello ma nota mente questo non può essere trattata come un array normale, ad esempio Array.isArray(ary)è false. La lunghezza è anche di sola lettura, quindi non è possibile inserire nuovi elementi come conary.push
MusikAnimal

In seguito tutti gli array digitati mantengono 0il loro valore predefinito.
jfunk

2
@MusikAnimal, Array.from(new Uint8Array(10))fornirà un array normale.
Tomas Langkaas,

@TomasLangkaas: Sì, ma un'altra risposta mostra che è circa 5 volte più lento rispetto 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. : /
Peter Cordes,

29

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]

23
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 whiledi solito è più efficiente rispetto for-in, forEache così via


3
La ivariabile locale non è estranea? lengthviene passato per valore, quindi dovresti essere in grado di diminuirlo direttamente.
Sean Bright,

3
Anche se all'inizio sembra fantastico, sfortunatamente è molto lento assegnare valori in un punto arbitrario in un arary (ad esempio arr[i] = value). È molto più veloce scorrere dall'inizio alla fine e utilizzarlo arr.push(value). È fastidioso, perché preferisco il tuo metodo.
Nick Brunt,

19

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.


5
Err ... non ci sono nulls in questo array -var x = new Array(7);
kangax,

5
In realtà, l'array non si riempie di nulla con il nuovo Array (n), nemmeno "indefinito", imposta semplicemente il valore della lunghezza degli array su n. Puoi verificarlo chiamando (new Array (1)). ForEach (...). forOgnuno non viene mai eseguito, diversamente da come lo si chiama su [non definito].
JussiR,

4
new Array(7)non non creare un array "pieno di indefinito". Crea un array vuoto con lunghezza 7.
RobG

1
Potresti voler riconsiderare parti della tua risposta poiché ciò che @RobG sta dicendo è fondamentale (se ciò che stavi dicendo è vero, la mappatura sarebbe stata molto più semplice)
Abdo

1
In questi giorni potresti farlo (new Array(10)).fill(0).
Javier de la Rosa,

18

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

1
Potresti anche lanciare la var array = []dichiarazione nella prima parte del ciclo for in realtà, separata solo da una virgola.
damianb,

Mi piace quel suggerimento di damianb, ma ricordati di mettere il compito e la virgola prima dell'incremento! `for (var i = 0; i <lunghezza; array [i] = val, i ++);
Punstress

Fai quello che manca a tutti gli altri per il tuo secondo, e imposta la lunghezza dell'array sul lengthvalore già assegnato in modo che non cambi continuamente. Ho portato una serie di 1 milione di zero da 40ms a 8 sulla mia macchina.
Jonathan Gray,

Mi sembra di ottenere un aumento della velocità del 10-15% quando refactoring questa soluzione in una sola linea. for (i = 0, array = []; i < length; ++i) array[i] = val;.. Meno blocchi? ... comunque, anche ... se imposto la array.lengthlunghezza 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 forloop ... ma l'init non è più necessario, quindi whilesembra essere più veloce in questa versione)
Pimp Trizkit

Lo noterò anche nei miei test. In un numero decente dei miei casi di test, la versione finale sopra sembra funzionare da 3 a ben 10 volte più veloce ... Non sono così sicuro del perché ... (diverse dimensioni di array testate tra Chrome e FF)
Pimp Trizkit

13

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.


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

3
Puoi anche usare new Array(size+1).join("x").split("x").map(function() { return 0; })per ottenere numeri reali
Yuval

6
@Yuval Or justnew Array(size+1).join('0').split('').map(Number)
Paul

11

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 fillmetodo. 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]


10

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

  1. Frederik Gottlieb - 6.43
  2. Sam Barnum - 4,83
  3. Eli - 3.68
  4. Giosuè 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 Node.js. Lo sospetto vicino al benchmark di Chrome.


Questo è il modo più efficiente per le mie dita e per i miei occhi. Ma è molto lento per Chrome (d'accordo con quel jsperf. 99% più lento).
Orwellophile

1
Mi chiedo se il problema su Mac del tuo amico era legato a: stackoverflow.com/questions/39129200/... o forse Array.slice non ha gestito l'UInt8Array e perdite di memoria Non inizializzato? (un problema di sicurezza!).
robocat,

@robocat Buona cattura! Se me lo ricordo bene stavamo usando Node.js 0.6 o 0.8. Abbiamo pensato a qualche tipo di perdita ma non siamo riusciti a riprodurlo con lo stack di produzione, quindi abbiamo deciso di ignorarlo.
durum

10

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

Sono contento che tu abbia aggiunto questa risposta, mentre uso il trattino basso e sapevo che c'era qualcosa per questo ... ma non ero ancora riuscito a trovarla. Vorrei solo poter creare matrici di oggetti usando questo
PandaWood,

@PandaWood _.range (0, length -1, 0) .map (Object.new), credo.
Djechlin,

Dovrei essere _.range(0, length, 0), credo. Lodash è esclusivo del valore finale
user4815162342

9

Soluzione ES6:

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

8

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ù varsenza un motivo molto speciale quando si utilizza ES6 o versioni successive. Utilizzare constper variabili che non verranno ridefinite e letper 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.


8

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

Esempio

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


6

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.


5
Né il più veloce né il più breve ma un bel contributo alla diversità delle soluzioni.
7vujy0f0hy


4

Questa concatversione è 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 concatse):

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

2
Ok, selvaggio. È VELOCEMENTE più veloce dell'uso del nuovo array (len). MA! Sto vedendo in Chrome che le letture successive a quei dati impiegano molto più tempo. Ecco alcuni timestamp per mostrare cosa intendo: (Uso della nuova matrice (len)) 0.365: Creazione della matrice 4.526: Esecuzione della convoluzione 10.75: Convoluzione completa (Uso della concat) 0.339: Creazione della matrice 0.591: Esecuzione della convoluzione // OMG, MODO più veloce 18.056: Convoluzione completata
Brooks,

4

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



4

Potrebbe essere utile sottolineare che Array.prototype.fillera 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;
  };
}

4

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;

2
Dato che la lunghezza è una variabile definita n, questa sarebbe più breve:for(var a=[];n--;a[n]=0);
Tomas Langkaas


3

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? :-)


Puoi renderlo più veloce cambiandolo in while (len--).. ho preso i miei tempi di elaborazione da circa 60ms a circa 54ms
nickf

La risposta di Matthew Crumbly batte ancora in realtà questo (30ms)!
Nickf

3

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


L'ho appena provato: il secondo metodo ( b = []...) è del 10-15% più veloce del primo, ma è 10 volte più lento della risposta di Joshua.
Nickf

So che questo è un post antico . Ma forse è ancora interessante per gli altri (come me). Pertanto, vorrei suggerire un'adesione alla funzione prototipo: includere un else {this.length=n;}dopo il this.lengthsegno di spunta. Ciò ridurrà un array già esistente, se necessario, quando initlo reinizializzerà a una lunghezza diversa n.
auto 10m

2

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

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.