Der effizienteste Weg, um ein mit Null gefülltes JavaScript-Array zu erstellen?


602

Was ist der effizienteste Weg, um ein Array mit beliebiger Länge und Null in JavaScript zu erstellen?


7
Einige aktuelle Daten dazu: jsperf.com/zeroarrayjs
Web_Designer

7
ES6-Füllung ermöglicht dies nativ.
Salvador Dali

1
arr = neues Array (Länge + 1) .joint (Zeichen) .split ('');
Jordan Stefanelli

4
UPDATE 2016 : Ein weiterer benutzerdefinierter Benchmark hier: jsfiddle.net/basickarl/md5z0Lqq
K - Die Toxizität in SO nimmt zu.

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

Antworten:


543

ES6 stellt vor Array.prototype.fill. Es kann folgendermaßen verwendet werden:

new Array(len).fill(0);

Ich bin mir nicht sicher, ob es schnell ist, aber ich mag es, weil es kurz und selbstbeschreibend ist.

Es ist immer noch nicht im IE ( Kompatibilität prüfen ), aber es ist eine Polyfüllung verfügbar .


15
Füllen ist schnell. new Array(len)ist schmerzhaft langsam. (arr = []).length = len; arr.fill(0);ist ungefähr die schnellste Lösung, die ich jemals gesehen habe ... oder zumindest gebunden
Pimp Trizkit

7
@PimpTrizkit arr = Array(n)und (arr = []).length = nverhalten sich entsprechend der Spezifikation identisch. In einigen Implementierungen könnte man schneller sein, aber ich glaube nicht, dass es einen großen Unterschied gibt.
Oriol

2
Nun, ich habe angefangen, es mit mehrdimensionalen Arrays zu testen, und es schien meine Testfälle erheblich zu beschleunigen. Nachdem ich gerade einige weitere Tests mit FF41 und Chrome45.0.2454.99 m durchgeführt habe. Ja, ich glaube, ich brauchte wirklich mehr Platz, um mich zu erklären. Die meisten meiner Tests waren voreingenommen, wie ich zugeben werde. Aber probieren Sie es aus. Definieren Sie eine Variable vor und verwenden Sie nur diese Linie (arr = []).length = 1000; gegen arr = new Array(1000);Geschwindigkeitstest in Chrome und FF ... das newist furchtbar langsam. Nun, für Array-Längen kleiner ... sagen wir <50 oder so ungefähr ... dann new Array()scheint es besser zu funktionieren. Aber ..
Pimp Trizkit

4
... Ich gebe zu, dass ich diesen Teil verpasst habe ... wenn ich die zweite Zeile zum Test hinzufüge ... arr.fill(0) ... ändert sich alles irgendwie. Jetzt ist die Verwendung new Array()in den meisten Fällen schneller, außer wenn Sie Arraygrößen> 100000 erreichen ... Dann können Sie beginnen, die Geschwindigkeitssteigerung wieder zu sehen. Aber wenn Sie es nicht mit Nullen vorfüllen müssen und Standard-Falisy von leeren Arrays verwenden können. Dann (arr = []).length = xist in meinen Testfällen die meiste Zeit verrückt schnell.
Pimp Trizkit

4
Beachten Sie, dass zum Durchlaufen des Arrays (z. B. map oder forEach) die Werte festgelegt werden müssen , da sonst diese Indizes übersprungen werden. Die von Ihnen festgelegten Werte können beliebig sein - auch undefiniert. Beispiel: try new Array(5).forEach(val => console.log('hi'));vs new Array(5).fill(undefined).forEach(val => console.log('hi'));.
ArneHugo

387

Obwohl dies ein alter Thread ist, wollte ich meine 2 Cent hinzufügen. Ich bin mir nicht sicher, wie langsam / schnell das ist, aber es ist ein schneller Einzeiler. Folgendes mache ich:

Wenn ich eine Nummer vorab ausfüllen möchte:

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

Wenn ich mit einer Zeichenfolge vorfüllen möchte:

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

Andere Antworten haben vorgeschlagen:

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

Wenn Sie jedoch 0 (die Zahl) und nicht "0" (Null innerhalb einer Zeichenfolge) möchten, können Sie Folgendes tun:

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

6
Gute Antwort! Können Sie bitte den Trick mit erklären Array.apply(null, new Array(5)).map(...)? Weil einfach zu tun (neues Array (5)). Map (...) nicht funktioniert, wie die Spezifikation sagt
Dmitry Pashkevich

36
(Übrigens brauchen wir das nicht wirklich new) Wenn Sie dies tun Array(5), erstellen Sie ein Objekt, das irgendwie so aussieht: { length: 5, __proto__: Array.prototype }- versuchen Sie es console.dir( Array(5) ). Beachten Sie, dass es keine Eigenschaften hat 0,1 , 2etc. Aber wenn man , applydass zu dem ArrayKonstruktor, es ist wie wenn man sagt Array(undefined, undefined, undefined, undefined, undefined). Und du bekommst ein Objekt, das irgendwie aussieht { length: 5, 0: undefined, 1: undefined...}. maparbeitet auf die Eigenschaften 0, 1etc. weshalb Ihr Beispiel nicht funktioniert, aber wenn Sie applyes tut.
zertosh

4
Der erste Parameter für .applyist eigentlich das, was Sie wollen this. Für diese Zwecke spielt das thiskeine Rolle - wir kümmern uns nur wirklich um die Parameter-Spreizfunktion von .apply- also kann es ein beliebiger Wert sein. Ich mag es, nullweil es billig ist, Sie es wahrscheinlich nicht verwenden möchten {}oder []weil Sie ein Objekt ohne Grund instanziieren würden.
zertosh

2
Auch das Initialisieren mit Größe + Zuweisen ist viel schneller als Push. Siehe Testfall jsperf.com/zero-fill-2d-array
Colin

2
Was ist mit Array.apply (null, Array (5)). map (x => 0)? Es ist etwas kürzer!
Arch Linux Tux

97

Elegante Möglichkeit, ein Array mit vorberechneten Werten zu füllen

Hier ist eine andere Möglichkeit, ES6 zu verwenden, die bisher noch niemand erwähnt hat:

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

Es funktioniert durch Übergeben einer Kartenfunktion als zweiten Parameter von Array.from .

Im obigen Beispiel weist der erste Parameter ein Array von 3 Positionen zu, die mit dem Wert gefüllt sind, undefinedund dann ordnet die Lambda-Funktion jede dieser Positionen dem Wert zu0 .

Obwohl Array(len).fill(0)es kürzer ist, funktioniert es nicht, wenn Sie das Array zuerst mit Berechnungen füllen müssen (ich weiß, dass die Frage nicht danach gefragt hat, aber viele Leute suchen hier danach). .

Wenn Sie beispielsweise ein Array mit 10 Zufallszahlen benötigen:

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

Es ist prägnanter (und eleganter) als das Äquivalent:

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

Diese Methode kann auch verwendet werden, um Zahlenfolgen zu generieren, indem der im Rückruf angegebene Indexparameter genutzt wird:

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

Bonusantwort: Füllen Sie ein Array mit String repeat()

Da diese Antwort viel Aufmerksamkeit erhält, wollte ich auch diesen coolen Trick zeigen. Obwohl nicht so nützlich wie meine Hauptantwort, wird der noch nicht sehr bekannte, aber sehr nützliche String vorgestelltrepeat() Methode eingeführt. Hier ist der Trick:

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

Cool was? repeat()ist eine sehr nützliche Methode, um eine Zeichenfolge zu erstellen, bei der die ursprüngliche Zeichenfolge eine bestimmte Anzahl von Malen wiederholt wird. Danach split()erstellt er ein Array für uns, das dann map()auf die gewünschten Werte eingestellt wird. Aufteilung in Schritte:

> "?".repeat(10)
< "??????????"

> "?".repeat(10).split("")
< ["?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]

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

Viele Salon-Tricks in diesem Beitrag, aber hoffentlich keine, die den Produktionscode erreichen :)
Eric Grange

Obwohl der repeatTrick in der Produktion definitiv nicht erwünscht ist, Array.from()ist er vollkommen in Ordnung :-)
Lucio Paiva

Nicht wirklich, Array.from () erstellt hier im Grunde genommen ein Array, durchläuft es mit map (), ruft eine Funktion für jedes Element auf, um ein neues Array zu erstellen, und verwirft dann das erste Array ... Bei kleinen Arrays kann dies der Fall sein harmlos, für größere Arrays ist dies die Art von Muster, die dazu führt, dass Leute Browser "Memory Hogs" nennen :)
Eric Grange

Leute, die mit großen Arrays zu tun haben, sollten es definitiv besser wissen. Für gängige Apps ist es jedoch völlig in Ordnung, ein Aux-Array mit normaler Größe (bis zu 10.000 Elemente) zu erstellen, das sofort entsorgt wird (dies dauert genauso lange, als hätten Sie die zusätzliche Array-Erstellung vermieden - getestet mit dem neuesten Chrome). In solchen Fällen ist die Lesbarkeit wichtiger als winzige Leistungsoptimierungen. Ungefähr zur O (n) -Zeit ist es notwendig, wenn Sie für jedes Element etwas anderes berechnen müssen (das Hauptthema meiner Antwort). Diese Diskussion ist sehr interessant, ich bin froh, dass Sie sie angesprochen haben!
Lucio Paiva

88

Zusamenfassend

Schnellste Lösung

let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;

Kürzeste (handliche) Lösung (3x langsamer für kleine Arrays, etwas langsamer für große (am langsamsten bei Firefox))

Array(n).fill(0)


Einzelheiten

Heute 2020.06.09 führe ich Tests unter macOS High Sierra 10.13.6 in den Browsern Chrome 83.0, Firefox 77.0 und Safari 13.1 durch. Ich teste ausgewählte Lösungen für zwei Testfälle

  • kleines Array - mit 10 Elementen - können Sie einen Test durchführen HIER
  • Große Arrays - mit 1M Elementen - können Sie HIER testen

Schlussfolgerungen

  • Lösung basierend auf new Array(n)+for (N) ist die schnellste Lösung für kleine und große Arrays (außer Chrome, aber immer noch sehr schnell) und wird als schnelle browserübergreifende Lösung empfohlen
  • Eine auf new Float32Array(n)(I) basierende Lösung gibt ein nicht typisches Array zurück (z. B. können Sie nicht aufrufenpush(..) ), sodass ich die Ergebnisse nicht mit anderen Lösungen vergleiche. Diese Lösung ist jedoch etwa 10 bis 20 Mal schneller als andere Lösungen für große Arrays in allen Browsern
  • Lösungen basierend auf for (L, M, N, O) basieren, sind für kleine Arrays schnell
  • Lösungen basierend auf fill (B, C) , sind in Chrome und Safari schnell, in Firefox für große Arrays jedoch überraschend langsam. Sie sind mittelschnell für kleine Arrays
  • Eine auf Array.apply(P) basierende Lösung löst einen Fehler für große Arrays aus

Geben Sie hier die Bildbeschreibung ein

Code und Beispiel

Der folgende Code enthält Lösungen für Messungen

Beispielergebnisse für Chrome

Geben Sie hier die Bildbeschreibung ein


Ich habe gerade einige Tests auf Chrome 77 ausgeführt und eine einfache Schleife mit push () ist doppelt so schnell wie fill () ... Ich frage mich, welche subtilen Nebenwirkungen von fill () eine effizientere Implementierung verhindern.
Eric Grange

@EricGrange Ich aktualisiere die Antwort - unten aktualisiere ich den Link zu Benchamrk mit Ihrem Vorschlag: Fall P let a=[]; for(i=n;i--;) a.push(0);- aber es ist 4x langsamer als fill(0)- also werde ich das Bild in diesem Fall nicht einmal aktualisieren.
Kamil Kiełczewski

2
Schöne Messungen. Analyse: G ist langsam, da die Größe des Arrays bei jeder Iteration geändert wird. Eine Größenänderung bedeutet, dass eine neue Speicherzuweisung vorgenommen wird. A, B, M schnell, da die Dimensionierung nur einmal erfolgt. +1
Roland

@gman eigentlich hast du recht - ich verstehe nicht, wie ich es hätte verpassen können - ich führe neue Tests durch und aktualisiere die Antwort mit weiteren Details - danke :)
Kamil Kiełczewski

63

Die bereits erwähnte ES 6-Füllmethode sorgt dafür. Die meisten modernen Desktop-Browser unterstützen bereits heute die erforderlichen Array-Prototyp-Methoden (Chromium, FF, Edge und Safari) [ 1 ]. Sie können Details zu MDN nachschlagen . Ein einfaches Anwendungsbeispiel ist

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

Angesichts der aktuellen Browserunterstützung sollten Sie vorsichtig sein, es sei denn, Sie sind sicher, dass Ihre Zielgruppe moderne Desktop-Browser verwendet.


4
Wenn Sie einen Referenztyp eingeben, handelt es sich bei allen um dieselbe Referenz. neues Array (10) .fill (null) .map (() => []) wäre ein prägnanter Weg, um dies zu
umgehen

4
UPDATE 2016 : Diese Methode bläst alles andere aus dem Wasser, klicken Sie hier für Benchmarks: jsfiddle.net/basickarl/md5z0Lqq
K - Die Toxizität in SO wächst.

Dies funktioniert für Arrays. a = Array(10).fill(null).map(() => { return []; });
Andy

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

50

Hinweis hinzugefügt August 2013, aktualisiert Februar 2015: Die folgende Antwort aus dem Jahr 2009 bezieht sich auf den generischen ArrayTyp von JavaScript . Es bezieht sich nicht auf die neueren typisierten Arrays, die in ES2015 definiert sind [und jetzt in vielen Browsern verfügbar sind], wie Int32Arrayund so. Beachten Sie auch, dass ES2015 fillsowohl Arrays als auch typisierten Arrays eine Methode hinzufügt , die wahrscheinlich die effizienteste Methode ist, um sie zu füllen ...

Außerdem kann es bei einigen Implementierungen einen großen Unterschied machen, wie Sie das Array erstellen. Insbesondere die V8-Engine von Chrome versucht, ein hocheffizientes Array mit zusammenhängendem Speicher zu verwenden, wenn sie dies für möglich hält, und wechselt nur bei Bedarf zum objektbasierten Array.


Bei den meisten Sprachen wird es vorab zugewiesen und dann wie folgt mit Null gefüllt:

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

Aber , JavaScript - Arrays sind nicht wirklich Arrays , sie sind Schlüssel / Wert - Karten wie alle anderen JavaScript - Objekte, so gibt es keine „vorbelegt“ zu tun (Einstellung der Länge zuteilen nicht , dass viele Slots zu füllen), noch Gibt es einen Grund zu der Annahme, dass der Vorteil des Herunterzählens auf Null (der nur dazu dient, den Vergleich in der Schleife schnell zu machen) nicht durch das Hinzufügen der Schlüssel in umgekehrter Reihenfolge aufgewogen wird, wenn die Implementierung möglicherweise die Handhabung der Schlüssel optimiert hat Bezogen auf Arrays in der Theorie werden Sie sie im Allgemeinen in der richtigen Reihenfolge ausführen.

Tatsächlich wies Matthew Crumley darauf hin, dass das Herunterzählen in Firefox deutlich langsamer ist als das Hochzählen, ein Ergebnis, das ich bestätigen kann - es ist der Array-Teil davon (das Schleifen auf Null ist immer noch schneller als das Schleifen bis zu einem Grenzwert in einer Var). Anscheinend ist das Hinzufügen der Elemente zum Array in umgekehrter Reihenfolge eine langsame Operation in Firefox. Tatsächlich variieren die Ergebnisse je nach JavaScript-Implementierung erheblich (was nicht allzu überraschend ist). Hier ist eine schnelle und schmutzige Testseite (unten) für Browser-Implementierungen (sehr schmutzig, gibt während der Tests nicht nach, bietet daher nur minimales Feedback und läuft unter Einhaltung der Skript-Zeitlimits). Ich empfehle, zwischen den Tests zu aktualisieren. FF verlangsamt sich (zumindest) bei wiederholten Tests, wenn Sie dies nicht tun.

Die ziemlich komplizierte Version, die Array # concat verwendet, ist schneller als ein Straight Init auf FF, und zwar zwischen 1.000 und 2.000 Elementarrays. Bei der V8-Engine von Chrome gewinnt Straight Init jedoch jedes Mal ...

Hier ist die Testseite ( Live-Kopie ):

<!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>

Ich bin mir nicht sicher, ob das Rückwärtsfüllen hier von Bedeutung ist, da Sie nur auf Elemente zugreifen (diese nicht löschen) und bereits vorab zugewiesen haben. Liege ich falsch?
Triptychon

Der Punkt der Rückwärtsfüllung hat nicht speziell mit dem Array zu tun, sondern mit der Escape-Bedingung für die Weile - die Falsey 0 beendet die Schleife sehr effizient
Annakata

(obwohl ich gerade bemerkt habe, dass dieser Code das nicht wirklich nutzt)
annakata

@annakata, das können Sie hier nicht nutzen, da 0 ein gültiger Index ist.
Triptychon

@triptych: nicht wahr, alles was es braucht ist die richtige Reihenfolge - siehe meinen Beitrag
annakata

34

In der Standardeinstellung Uint8Array, Uint16Arrayund Uint32ArrayKlassen halten Nullen als Werte, so dass Sie keine komplexen Abfülltechniken benötigen, gerade tun:

var ary = new Uint8Array(10);

Alle Elemente des Arrays arysind standardmäßig Nullen.


5
Das ist schön , aber dieser Geist Notiz kann nicht das gleiche wie ein normales Array behandelt werden, zB Array.isArray(ary)ist false. Die Länge ist auch schreibgeschützt, so dass Sie keine neuen Elemente wie beiary.push
MusikAnimal

Fwiw alle typisierten Arrays behalten 0ihren Standardwert bei.
jfunk

2
@MusikAnimal Array.from(new Uint8Array(10))stellt ein normales Array bereit.
Tomas Langkaas

@TomasLangkaas: Ja, aber eine andere Antwort zeigt, dass das ungefähr 5x langsamer ist als Array(n).fill(0)in Chrome, wenn Sie wirklich ein JS-Array benötigen. Wenn Sie ein TypedArray verwenden können, ist dies sogar viel schneller als .fill(0), insbesondere wenn Sie den Standardinitialisiererwert von verwenden können 0. Es scheint keinen Konstruktor zu geben, der einen Füllwert und eine Länge annimmt, wie es C ++ getan std::vectorhat. Es scheint, dass Sie für jeden Wert ungleich Null ein typisiertes Array mit Null erstellen und es dann füllen müssen. : /
Peter Cordes

29

Wenn Sie ES6 verwenden, können Sie Array.from () wie folgt verwenden :

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

Hat das gleiche Ergebnis wie

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

weil

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

Beachten Sie, dass whilein der Regel effizienter als ist for-in, forEachusw.


3
Ist die ilokale Variable nicht irrelevant? lengthwird als Wert übergeben, sodass Sie ihn direkt dekrementieren können sollten.
Sean Bright

3
Obwohl dies auf den ersten Blick gut aussieht, ist es leider sehr langsam, Werte an einem beliebigen Punkt in einem Arary zuzuweisen (z arr[i] = value. B. ). Es ist viel schneller, von Anfang bis Ende durchzugehen und zu verwenden arr.push(value). Es ist ärgerlich, weil ich Ihre Methode bevorzuge.
Nick Brunt

19

mit Objektnotation

var x = [];

Null gefüllt? mögen...

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

gefüllt mit 'undefiniert' ...

var x = new Array(7);

Objektnotation mit Nullen

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

Nebenbei bemerkt, wenn Sie den Prototyp von Array ändern, beides

var x = new Array();

und

var y = [];

wird diese Prototyp-Modifikationen haben

Auf jeden Fall würde ich mich nicht allzu sehr um die Effizienz oder Geschwindigkeit dieses Vorgangs kümmern. Es gibt viele andere Dinge, die Sie wahrscheinlich tun werden, die weitaus verschwenderischer und teurer sind, als ein Array beliebiger Länge mit Nullen zu instanziieren.


5
Äh ... es gibt keine nulls in diesem Array -var x = new Array(7);
Kangax

5
Tatsächlich wird das Array mit nichts mit neuem Array (n) gefüllt, nicht einmal mit 'undefinierten'. Es setzt einfach den Wert für die Länge des Arrays auf n. Sie können dies überprüfen, indem Sie (new Array (1)). ForEach (...) aufrufen. forEach wird nie ausgeführt, anders als wenn Sie es auf [undefined] aufrufen.
JussiR

4
new Array(7)ist nicht ein Array „gefüllt mit undefinierten“ erstellen. Es erstellt ein leeres Array mit der Länge 7.
RobG

1
Vielleicht möchten Sie Teile Ihrer Antwort überdenken, da das, was @RobG sagt, kritisch ist (wenn das, was Sie gesagt haben, wahr ist, wäre die Zuordnung viel einfacher gewesen)
Abdo

1
In diesen Tagen könnten Sie tun (new Array(10)).fill(0).
Javier de la Rosa

18

Ich habe in IE 6/7/8, Firefox 3.5, Chrome und Opera alle Kombinationen von Vorzuweisung / Nichtvorzuweisung, Aufwärts- / Abwärts- und For / While-Schleifen getestet.

Die folgenden Funktionen waren in Firefox, Chrome und IE8 durchweg die schnellsten oder extremsten und in Opera und IE 6 nicht viel langsamer als die schnellsten. Sie sind meiner Meinung nach auch die einfachsten und klarsten. Ich habe mehrere Browser gefunden, in denen die while-Schleifenversion etwas schneller ist, daher füge ich sie auch als Referenz hinzu.

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

oder

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

1
Sie können die var array = []Deklaration auch in den ersten Teil der for-Schleife werfen, der nur durch ein Komma getrennt ist.
Damianb

Ich mag diesen Vorschlag von Damianb, aber denken Sie daran, die Zuweisung und das Komma vor die Inkrementierung zu setzen! `for (var i = 0; i <Länge; Array [i] = val, i ++);
Punstress

Tun Sie, was allen anderen bei Ihrem zweiten fehlt, und stellen Sie die Länge des Arrays auf den lengthbereits angegebenen Wert ein, damit es sich nicht ständig ändert. Brachte ein 1 Million langes Array von Nullen von 40 ms bis 8 auf meinen Computer.
Jonathan Gray

Ich scheine eine Geschwindigkeitssteigerung von 10-15% zu erzielen, wenn ich diese Lösung in einen Einzeiler umgestalte. for (i = 0, array = []; i < length; ++i) array[i] = val;.. Weniger Blöcke? ... jedenfalls auch ... wenn ich array.lengthdas neue Array auf die Länge setze ... ich bekomme eine weitere Geschwindigkeitssteigerung von 10% -15% in FF ... in Chrome scheint es die Geschwindigkeit zu verdoppeln -> var i, array = []; array.length = length; while(i < length) array[i++] = val;(war immer noch schneller, wenn ich es als forSchleife belassen habe ... aber der Init wird nicht mehr benötigt, so dass der whilebei dieser Version scheinbar schneller ist)
Pimp Trizkit

Ich werde das auch bei meinen Tests bemerken. In einer anständigen Anzahl meiner Testfälle scheint die obige endgültige Version 3x bis weit über 10x schneller zu sein ...
Ich bin

13

Wenn Sie während der Ausführung Ihres Codes viele mit Nullen gefüllte Arrays unterschiedlicher Länge erstellen müssen, besteht der schnellste Weg, dies zu erreichen, darin, einmal mit einer der in diesem Thema genannten Methoden ein Null-Array mit einer Länge zu erstellen von denen Sie wissen, dass sie niemals überschritten werden, und schneiden Sie das Array dann nach Bedarf auf.

Erstellen Sie beispielsweise (unter Verwendung der Funktion aus der oben gewählten Antwort zum Initialisieren des Arrays) ein mit Null gefülltes Array mit der Länge maxLength als Variable, die für den Code sichtbar ist, der Null-Arrays benötigt:

var zero = newFilledArray(maxLength, 0);

Schneiden Sie dieses Array jetzt jedes Mal in Scheiben, wenn Sie ein mit Null gefülltes Array mit der erforderlichen Länge benötigen. Länge < maxLänge :

zero.slice(0, requiredLength);

Ich habe während der Ausführung meines Codes tausende Male null gefüllte Arrays erstellt, was den Prozess enorm beschleunigte.


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

3
Sie können auch verwenden new Array(size+1).join("x").split("x").map(function() { return 0; }), um tatsächliche Zahlen zu erhalten
Yuval

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

11

Ich habe nichts gegen:

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

Von Zertosh vorgeschlagen, aber in einer neuen ES6- Array-Erweiterung können Sie dies nativ mit der fillMethode tun . Jetzt unterstützen IE Edge, Chrome und FF dies, überprüfen Sie jedoch die Kompatibilitätstabelle

new Array(3).fill(0)werde dir geben [0, 0, 0]. Sie können das Array mit einem beliebigen Wert wie new Array(5).fill('abc')(auch Objekten und anderen Arrays) füllen .

Darüber hinaus können Sie vorherige Arrays mit fill ändern:

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

was dir gibt: [1, 2, 3, 9, 9, 6]


10

Die Art und Weise, wie ich es normalerweise mache (und es ist erstaunlich schnell), ist die Verwendung Uint8Array. Beispiel: Erstellen eines mit Nullen gefüllten Vektors aus 1 Millionen Elementen:

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

Ich bin ein Linux-Benutzer und habe immer für mich gearbeitet, aber einmal hatte ein Freund, der einen Mac verwendete, einige Elemente ungleich Null. Ich dachte, seine Maschine funktioniert nicht richtig, aber hier ist der sicherste Weg, dies zu beheben:

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

Bearbeitet

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. Pulver - 0,67
  10. zertosh - FEHLER

Firefox 20.0

  1. Allen Rice - 1,85
  2. Joshua - 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. Pulver - 0,13
  10. zertosh - FEHLER

Ich vermisse den wichtigsten Test (zumindest für mich): den Node.js. Ich vermute, dass es nahe am Chrome-Benchmark liegt.


Dies ist der effizienteste Weg für meine Finger und für meine Augen. Aber es ist sehr, sehr langsam für Chrome (entsprechend jsperf. 99% langsamer).
Orwellophile

1
Ich frage mich, ob das Problem auf dem Mac Ihres Freundes mit folgendem zusammenhängt: stackoverflow.com/questions/39129200/… oder Array.slice hat das UInt8Array nicht verarbeitet und nicht initialisierten Speicher verloren? (ein Sicherheitsproblem!).
Robocat

@robocat Guter Fang! Wenn ich mich gut erinnere, haben wir Node.js 0.6 oder 0.8 verwendet. Wir haben über eine Art Leck nachgedacht, konnten es aber nicht mit dem Produktionsstapel reproduzieren, also haben wir uns entschlossen, es zu ignorieren.
Durum

10

Mit lodash oder Unterstrich

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

Oder wenn ein Array vorhanden ist und Sie ein Array mit derselben Länge möchten

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

Ich bin so froh, dass Sie diese Antwort hinzugefügt haben, da ich Unterstriche verwende, und ich wusste, dass es etwas dafür gibt ... aber ich konnte es noch nicht finden. Ich wünschte nur, ich könnte Arrays von Objekten mit diesem erstellen
PandaWood

@PandaWood _.range (0, Länge -1, 0) .map (Object.new), denke ich.
Djechlin

Sollte sein _.range(0, length, 0), glaube ich. Lodash ist exklusiv vom Endwert
user4815162342

9

ES6-Lösung:

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

8

Ab ECMAScript2016 gibt es eine klare Wahl für große Arrays.

Da diese Antwort bei Google-Suchanfragen immer noch ganz oben angezeigt wird, finden Sie hier eine Antwort für 2017.

Hier ist eine aktuelle jsbench mit ein paar Dutzend gängigen Methoden, darunter viele, die bisher zu dieser Frage vorgeschlagen wurden. Wenn Sie eine bessere Methode finden, fügen Sie diese bitte hinzu, teilen Sie sie und teilen Sie sie.

Ich möchte darauf hinweisen, dass es keinen wirklich effizientesten Weg gibt, ein Array mit beliebiger Länge und Null zu erstellen. Sie können die Geschwindigkeit oder die Klarheit und Wartbarkeit optimieren - je nach den Anforderungen des Projekts kann entweder die effizientere Wahl getroffen werden.

Bei der Optimierung der Geschwindigkeit möchten Sie: das Array mithilfe der Literal-Syntax erstellen; Legen Sie die Länge fest, initialisieren Sie die iterierende Variable und durchlaufen Sie das Array mithilfe einer while-Schleife. Hier ist ein Beispiel.

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

Eine andere mögliche Implementierung wäre:

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

Ich rate jedoch dringend davon ab, diese zweite Implantation in der Praxis zu verwenden, da sie weniger klar ist und es Ihnen nicht ermöglicht, das Block-Scoping für Ihre Array-Variable beizubehalten.

Diese sind deutlich schneller als das Füllen mit einer for-Schleife und etwa 90% schneller als die Standardmethode von

const arr = Array(n).fill(0);

Diese Füllmethode ist jedoch aufgrund ihrer Klarheit, Prägnanz und Wartbarkeit immer noch die effizienteste Wahl für kleinere Arrays. Der Leistungsunterschied wird Sie wahrscheinlich nicht töten, wenn Sie nicht viele Arrays mit Längen in der Größenordnung von Tausenden oder mehr erstellen.

Ein paar andere wichtige Hinweise. Die meisten Styleguides empfehlen, dass Sie varES6 oder höher nicht ohne besonderen Grund mehr verwenden . Verwendung constfür Variablen, die nicht neu definiert werden, und letfür Variablen, die neu definiert werden. Das MDN und der Airbnb Style Guide sind großartige Orte, um weitere Informationen zu Best Practices zu erhalten. Bei den Fragen ging es nicht um Syntax, aber es ist wichtig, dass JS-Neulinge diese neuen Standards kennen, wenn sie diese Unmengen alter und neuer Antworten durchsuchen.


8

So erstellen Sie ein völlig neues Array

new Array(arrayLength).fill(0);

Hinzufügen einiger Werte am Ende eines vorhandenen Arrays

[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]

Beispiel

//**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

Ich habe diese Methode nicht in Antworten gesehen, also hier ist sie:

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

Als Ergebnis erhalten Sie ein nullwertiges Array mit einer Länge von 200:

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

Ich bin mir über die Leistung dieses Codes nicht sicher, aber es sollte kein Problem sein, wenn Sie ihn für relativ kleine Arrays verwenden.


5
Weder der schnellste noch der kürzeste, aber ein schöner Beitrag zur Vielfalt der Lösungen.
7vujy0f0hy


4

Diese concat Version ist in meinen Tests auf Chrome (21.03.2013) viel schneller. Etwa 200 ms für 10.000.000 Elemente gegenüber 675 für Straight Init.

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: Wenn Sie Ihr Array mit Strings füllen möchten, ist dies eine kurze Möglichkeit (nicht ganz so schnell wie concat):

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

2
Ok, wild. Das ist viel schneller als mit einem neuen Array (len). ABER! Ich sehe in Chrome, dass das anschließende Lesen dieser Daten wesentlich länger dauert. Hier sind einige Zeitstempel, um zu zeigen, was ich meine: (Verwenden eines neuen Arrays (len)) 0.365: Erstellen von Array 4.526: Ausführen von Convolution 10.75: Fertigstellen von Convolution (Verwenden von concat) 0.339: Erstellen von Array 0.591: Ausführen von Convolution // OMG, WAY schneller 18.056: Faltung abgeschlossen
Brooks

4

Ich habe die großartige Antwort von TJ Crowder getestet und eine rekursive Zusammenführung gefunden, die auf der Concat-Lösung basiert, die alle seine Tests in Chrome übertrifft (ich habe keine anderen Browser getestet).

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

Rufen Sie die Methode mit auf makeRec(29).



4

Es könnte erwähnenswert sein, dass Array.prototype.filldies als Teil des ECMAScript 6 (Harmony) -Vorschlags hinzugefügt wurde . Ich würde mich lieber für die unten beschriebene Polyfüllung entscheiden, bevor ich andere im Thread erwähnte Optionen in Betracht ziehe.

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

Am kürzesten für Schleifencode

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;

Sichere var-Version

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

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

2
Da die Länge eine definierte Variable ist n, wäre diese kürzer:for(var a=[];n--;a[n]=0);
Tomas Langkaas


3

Meine schnellste Funktion wäre:

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

Das native Drücken und Verschieben zum Hinzufügen von Elementen zum Array ist viel schneller (ungefähr zehnmal) als das Deklarieren des Array-Bereichs und das Referenzieren jedes Elements, um seinen Wert festzulegen.

fyi: Ich bekomme durchweg schnellere Zeiten mit der ersten Schleife, die herunterzählt, wenn ich diese in Firebug (Firefox-Erweiterung) ausführe.

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

Ich bin interessiert zu wissen, was TJ Crowder daraus macht. :-)


Sie können es schneller machen, indem Sie es auf while (len--)... ändern . Ich habe meine Verarbeitungszeiten von ungefähr 60 ms auf ungefähr 54
ms

Matthew Crumblys Antwort übertrifft dies immer noch (30 ms)!
Nickf

3

Ich wusste, dass ich dieses Proto irgendwo hatte :)

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

Bearbeiten: Tests

Als Reaktion auf Joshua und andere Methoden habe ich mein eigenes Benchmarking durchgeführt und sehe völlig andere Ergebnisse als die gemeldeten.

Folgendes habe ich getestet:

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

Ergebnisse:

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

Meiner Meinung nach ist Push zwar im Allgemeinen langsamer, schneidet aber bei längeren Arrays in FF besser ab, im IE jedoch schlechter, was im Allgemeinen nur zum Kotzen ist (Quel-Überraschung).


Ich habe das gerade getestet: Die zweite Methode ( b = []...) ist 10-15% schneller als die erste, aber mehr als zehnmal langsamer als Joshuas Antwort.
Nickf

Ich weiß, dass dies ein alter Beitrag ist . Aber vielleicht ist es für andere (wie mich) immer noch von Interesse. Daher möchte ich eine Ergänzung zur Prototypfunktion vorschlagen: Fügen Sie else {this.length=n;}nach dem this.lengthCheck ein. Dadurch wird ein bereits vorhandenes Array bei Bedarf verkürzt, wenn inites auf eine andere Länge neu initialisiert wird n.
Autos10m

2

Anonyme Funktion:

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

Etwas kürzer mit for-Schleife:

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

Funktioniert mit jedem Object, ändern Sie einfach, was drin ist this.push().

Sie können die Funktion sogar speichern:

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

Nennen Sie es mit:

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

Hinzufügen von Elementen zu einem bereits vorhandenen Array:

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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.