Le moyen le plus efficace de créer un tableau JavaScript rempli de zéro?

602

Quelle est la façon la plus efficace de créer un tableau rempli de longueur arbitraire en JavaScript?

dil
la source
7
Quelques données réelles à ce sujet: jsperf.com/zeroarrayjs
Web_Designer
7
Le remplissage ES6 permet de le faire de manière native.
Salvador Dali
1
arr = nouveau tableau (longueur + 1) .joint (caractère) .split ('');
Jordan Stefanelli
4
MISE À JOUR 2016 : Un autre benchmark personnalisé ici: jsfiddle.net/basickarl/md5z0Lqq
K - La toxicité dans le SO augmente.
1
let i = 0; Array.from(Array(10), ()=>i++);
Bart Hoekstra

Réponses:

543

ES6 présente Array.prototype.fill. Il peut être utilisé comme ceci:

new Array(len).fill(0);

Je ne sais pas si c'est rapide, mais je l'aime parce qu'il est court et auto-descriptif.

Ce n'est toujours pas dans IE ( vérifiez la compatibilité ), mais il existe un polyfill disponible .

Oriol
la source
15
le remplissage est rapide. new Array(len)est douloureusement lent. (arr = []).length = len; arr.fill(0);est la solution la plus rapide que j'aie jamais vue ... ou du moins à égalité
Pimp Trizkit
7
@PimpTrizkit arr = Array(n)et (arr = []).length = nse comportent de manière identique selon les spécifications. Dans certaines implémentations, on pourrait être plus rapide, mais je ne pense pas qu'il y ait une grande différence.
Oriol
2
Eh bien, j'ai commencé à le tester avec des tableaux multidimensionnels et cela a semblé accélérer considérablement mes cas de test. Après avoir fait quelques tests supplémentaires sur FF41 et Chrome45.0.2454.99 m. Oui, je suppose que j'avais vraiment besoin de plus d'espace pour m'expliquer. La plupart de mes tests étaient des biais, je l'admets. Mais regardez ça. Prédéfinir un var et en utilisant uniquement cette ligne (arr = []).length = 1000; contre la arr = new Array(1000);vitesse, testez-le dans Chrome et FF ... newc'est terriblement lent. Maintenant, pour des longueurs de tableau plus petites .. disons <50 ou il y a environ ... alors new Array()cela semble mieux fonctionner. Mais ..
Pimp Trizkit
4
... J'avoue que j'ai raté cette partie ... quand j'ajoute la deuxième ligne au test ... arr.fill(0) ... tout change en quelque sorte. Maintenant, l'utilisation new Array()est plus rapide dans la plupart des cas, sauf lorsque vous atteignez des tailles de tableau> 100 000 ... Ensuite, vous pouvez recommencer à voir l'augmentation de la vitesse. Mais si vous n'avez pas besoin de le pré-remplir avec des zéros et que vous pouvez utiliser une falisy standard de tableaux vides. Ensuite, (arr = []).length = xc'est fou rapide dans mes cas de test la plupart du temps.
Pimp Trizkit
4
Notez que pour parcourir le tableau (par exemple, map ou forEach), les valeurs doivent être définies , sinon il sautera ces index. Les valeurs que vous définissez peuvent être celles que vous souhaitez - même indéfinies. Exemple: essayer new Array(5).forEach(val => console.log('hi'));vs new Array(5).fill(undefined).forEach(val => console.log('hi'));.
ArneHugo
387

Bien que ce soit un vieux fil, je voulais y ajouter mes 2 cents. Je ne sais pas à quel point c'est lent / rapide, mais c'est une doublure rapide. Voici ce que je fais:

Si je veux pré-remplir un numéro:

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

Si je veux pré-remplir avec une chaîne:

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

D'autres réponses ont suggéré:

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

mais si vous voulez 0 (le nombre) et non "0" (zéro dans une chaîne), vous pouvez faire:

new Array(5+1).join('0').split('').map(parseFloat)
// [0, 0, 0, 0, 0]
zertosh
la source
6
Très bonne réponse! Pouvez-vous s'il vous plaît expliquer l'astuce avec Array.apply(null, new Array(5)).map(...)? Parce que simplement faire (nouveau tableau (5)). Map (...) ne fonctionnera pas comme le dit la spécification
Dmitry Pashkevich
36
(btw, nous n'en avons pas vraiment besoin new) Quand vous le faites, Array(5)vous créez un objet qui ressemble un peu à ceci: { length: 5, __proto__: Array.prototype }- essayez console.dir( Array(5) ). Notez qu'il n'a pas de propriétés 0, 1, 2, etc. Mais quand vous applyque jusqu'au Arrayconstructeur, il est comme dire Array(undefined, undefined, undefined, undefined, undefined). Et vous obtenez un objet qui ressemble un peu { length: 5, 0: undefined, 1: undefined...}. mapfonctionne sur les propriétés 0, 1etc. c'est pourquoi votre exemple ne fonctionne pas, mais quand vous l'utilisez apply.
zertosh
4
Le premier paramètre de .applyest en fait ce que vous voulez que ce thissoit. À ces fins, cela thisn'a pas d'importance - nous ne nous soucions vraiment que de la "caractéristique" de répartition des paramètres de .apply- il peut donc s'agir de n'importe quelle valeur. J'aime nullparce que c'est bon marché, vous ne voulez probablement pas l'utiliser {}ou []puisque vous instancieriez un objet sans raison.
zertosh
2
Initialiser également avec size + assign est beaucoup plus rapide que push. Voir le cas de test jsperf.com/zero-fill-2d-array
Colin
2
qu'en est-il de Array.apply (null, Array (5)). map (x => 0)? C'est un peu plus court!
Arch Linux Tux
97

Manière élégante de remplir un tableau avec des valeurs précalculées

Voici une autre façon de le faire en utilisant ES6 que personne n'a mentionné jusqu'à présent:

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

Il fonctionne en passant une fonction de carte comme deuxième paramètre de Array.from.

Dans l'exemple ci-dessus, le premier paramètre alloue un tableau de 3 positions remplies avec la valeur undefined, puis la fonction lambda mappe chacun d'eux à la valeur 0.

Bien que ce Array(len).fill(0)soit plus court, cela ne fonctionne pas si vous devez remplir le tableau en faisant d'abord du calcul (je sais que la question ne l'a pas demandé, mais beaucoup de gens se retrouvent ici à le chercher) .

Par exemple, si vous avez besoin d'un tableau avec 10 nombres aléatoires:

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

C'est plus concis (et élégant) que l'équivalent:

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

Cette méthode peut également être utilisée pour générer des séquences de nombres en tirant parti du paramètre d'index fourni dans le rappel:

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

Réponse bonus: remplir un tableau à l'aide de String repeat()

Étant donné que cette réponse reçoit beaucoup d'attention, je voulais également montrer ce truc cool. Bien que pas aussi utile que ma réponse principale, présentera la repeat()méthode String encore peu connue, mais très utile . Voici l'astuce:

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

Cool hein? repeat()est une méthode très utile pour créer une chaîne qui est la répétition de la chaîne d'origine un certain nombre de fois. Après cela, split()crée un tableau pour nous, qui est ensuite map()suivi des valeurs que nous voulons. Décomposer en plusieurs étapes:

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

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

> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]
Lucio Paiva
la source
Beaucoup de trucs de salon dans ce post, mais j'espère qu'aucun n'atteindra le code de production :)
Eric Grange
Bien que l' repeatastuce ne soit certainement pas recherchée en production, elle Array.from()est parfaitement bien :-)
Lucio Paiva
Pas vraiment, Array.from () ici crée essentiellement un tableau, l'itère avec map (), appelle une fonction sur chaque élément pour créer un nouveau tableau, puis supprime le premier tableau ... Pour un petit tableau, cela peut être inoffensif, pour les tableaux plus grands, c'est le genre de motif qui fait que les gens appellent les navigateurs des "porcs de la mémoire" :)
Eric Grange
Les personnes confrontées à de grandes baies devraient certainement être mieux informées que cela. Pour les applications courantes, cependant, la création d'un tableau auxiliaire de taille normale (jusqu'à 10 000 éléments) qui sera immédiatement supprimé est parfaitement correcte (prend le même temps que si vous évitiez la création d'un tableau supplémentaire - testé avec le dernier Chrome). Pour de tels cas, la lisibilité devient plus importante que de minuscules optimisations de performances. À propos du temps O (n), c'est nécessaire si vous avez besoin de calculer quelque chose de différent pour chaque élément (le sujet principal de ma réponse). Cette discussion est très intéressante, heureuse que vous l'ayez soulevée!
Lucio Paiva du
88

En bref

Solution la plus rapide

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

Solution la plus courte (pratique) (3 fois plus lente pour les petites baies, légèrement plus lente pour les grandes (la plus lente sur Firefox))

Array(n).fill(0)


Détails

Aujourd'hui 2020.06.09, j'effectue des tests sur macOS High Sierra 10.13.6 sur les navigateurs Chrome 83.0, Firefox 77.0 et Safari 13.1. Je teste les solutions choisies pour deux cas de test

  • petit tableau - avec 10 éléments - vous pouvez effectuer le test ICI
  • grands tableaux - avec 1M d'éléments - vous pouvez effectuer le test ICI

Conclusions

  • La solution basée sur new Array(n)+for(N) est la solution la plus rapide pour les petites baies et les grandes baies (sauf Chrome mais toujours très rapide là-bas) et elle est recommandée comme solution multi-navigateur rapide
  • la solution basée sur new Float32Array(n)(I) renvoie un tableau non typique (par exemple, vous ne pouvez pas l'appeler push(..)) donc je ne compare pas ses résultats avec d'autres solutions - cependant cette solution est environ 10-20x plus rapide que les autres solutions pour les grands tableaux sur tous les navigateurs
  • les solutions basées sur for(L, M, N, O) sont rapides pour les petits tableaux
  • les solutions basées sur fill(B, C) sont rapides sur Chrome et Safari mais étonnamment plus lentes sur Firefox pour les grands tableaux. Ils sont moyennement rapides pour les petits tableaux
  • solution basée sur Array.apply(P) génère une erreur pour les grands tableaux

entrez la description de l'image ici

Code et exemple

Le code ci-dessous présente les solutions utilisées dans les mesures

Exemple de résultats pour Chrome

entrez la description de l'image ici

Kamil Kiełczewski
la source
Je viens de faire quelques tests sur Chrome 77 et une simple boucle avec push () est deux fois plus rapide que fill () ... Je me demande quels effets secondaires subtils de fill () empêchent une implémentation plus efficace?
Eric Grange
@EricGrange Je mets à jour la réponse - en bas, je mets à jour le lien vers benchamrk avec votre proposition: cas P let a=[]; for(i=n;i--;) a.push(0);- mais il est 4 fois plus lent que fill(0)- donc je ne mettrai même pas à jour l'image avec ce cas.
Kamil Kiełczewski
2
Belles mesures. Analyse: G est lent à cause du redimensionnement du tableau à chaque itération, et redimensionner signifie faire une nouvelle allocation de mémoire. A, B, M rapide car le dimensionnement n'est effectué qu'une seule fois. +1
Roland
63

La méthode de remplissage ES 6 déjà mentionnée s'en occupe bien. La plupart des navigateurs de bureau modernes prennent déjà en charge les méthodes de prototype de baie requises à ce jour (Chromium, FF, Edge et Safari) [ 1 ]. Vous pouvez rechercher des détails sur MDN . Un exemple d'utilisation simple est

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

Compte tenu de la prise en charge actuelle du navigateur, vous devez être prudent si vous n'êtes pas sûr que votre public utilise des navigateurs de bureau modernes.

Gerald Senarclens de Grancy
la source
4
Si vous remplissez avec un type de référence, ce sera la même référence pour chacun d'eux. new Array (10) .fill (null) .map (() => []) serait un moyen succinct de contourner ce
problème
4
MISE À JOUR 2016 : Cette méthode souffle tout le reste hors de l'eau, cliquez ici pour les repères: jsfiddle.net/basickarl/md5z0Lqq
K - La toxicité dans le SO augmente.
cela fonctionnera pour les tableaux. a = Array(10).fill(null).map(() => { return []; });
Andy
2
@AndrewAnthonyGerst Terser:a = Array(10).fill(0).map( _ => [] );
Phrogz
50

Note ajoutée août 2013, mise à jour février 2015: La réponse ci-dessous de 2009 concerne le Arraytype générique de JavaScript . Il ne concerne pas les plus récents typés tableaux définis dans ES2015 [et disponible maintenant dans de nombreux navigateurs], comme Int32Arrayet autres. Notez également que ES2015 ajoute une fillméthode aux tableaux et aux tableaux typés , ce qui est probablement le moyen le plus efficace de les remplir ...

En outre, cela peut faire une grande différence pour certaines implémentations dans la façon dont vous créez le tableau. Le moteur V8 de Chrome, en particulier, essaie d'utiliser un tableau de mémoire contiguë très efficace s'il le pense, en ne basculant sur le tableau basé sur les objets que lorsque cela est nécessaire.


Avec la plupart des langues, il s'agirait d'une pré-allocation, puis d'un remplissage nul, comme ceci:

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

Mais , les tableaux JavaScript ne sont pas vraiment des tableaux , ce sont des cartes clé / valeur comme tous les autres objets JavaScript, donc il n'y a pas de "pré-allocation" à faire (la définition de la longueur n'alloue pas autant d'emplacements à remplir), ni y a-t-il une raison de croire que l'avantage du compte à rebours à zéro (qui est juste de rendre la comparaison rapide dans la boucle) n'est pas contrebalancé par l'ajout des clés dans l'ordre inverse alors que la mise en œuvre pourrait bien avoir optimisé leur gestion des clés liés aux tableaux sur la théorie, vous les ferez généralement dans l'ordre.

En fait, Matthew Crumley a souligné que le décompte est nettement plus lent sur Firefox que le décompte, un résultat que je peux confirmer - c'est la partie du tableau (le bouclage jusqu'à zéro est toujours plus rapide que le bouclage jusqu'à une limite dans une var). Apparemment, l'ajout des éléments au tableau dans l'ordre inverse est une opération lente sur Firefox. En fait, les résultats varient beaucoup selon l'implémentation de JavaScript (ce qui n'est pas si surprenant). Voici une page de test rapide et sale (ci-dessous) pour les implémentations de navigateur (très sale, ne cède pas pendant les tests, fournit donc un retour minimal et ne respectera pas les limites de temps du script). Je recommande de rafraîchir entre les tests; FF (au moins) ralentit les tests répétés si vous ne le faites pas.

La version assez compliquée qui utilise Array # concat est plus rapide qu'un init direct sur FF entre 1 000 et 2 000 tableaux d'éléments. Cependant, sur le moteur V8 de Chrome, l'initialisation droite l'emporte à chaque fois ...

Voici la page de test ( copie en direct ):

<!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>
TJ Crowder
la source
Vous n'êtes pas sûr que le remplissage en arrière importerait ici, étant donné que vous n'accédez qu'aux éléments (pas les supprimer) et que vous avez déjà pré-alloué. Ai-je tort?
Triptyque
le point du remplissage arrière n'est pas particulièrement lié à la matrice, c'est à la condition d'échappement pour le moment - le falsey 0 termine la boucle très efficacement
annakata
(même si je viens de remarquer que ce code n'en fait pas vraiment usage)
annakata
@annakata, vous ne pouvez pas l'utiliser ici, car 0 est un index valide.
Triptyque
@triptych: pas vrai, tout ce qu'il faut, c'est le bon ordre - voir mon post
annakata
34

Par défaut Uint8Array, Uint16Arrayet les Uint32Arrayclasses conservent les zéros comme valeurs, vous n'avez donc pas besoin de techniques de remplissage complexes, faites simplement:

var ary = new Uint8Array(10);

tous les éléments du tableau aryseront des zéros par défaut.

impasse
la source
5
C'est agréable , mais note l' esprit que ce ne peut pas être traité comme un tableau normal, par exemple , Array.isArray(ary)est false. La longueur est également en lecture seule, vous ne pouvez donc pas y insérer de nouveaux éléments comme avecary.push
MusikAnimal
Fwiw tous les tableaux typés conservent 0leur valeur par défaut.
jfunk
2
@MusikAnimal, Array.from(new Uint8Array(10))fournira un tableau normal.
Tomas Langkaas
@TomasLangkaas: Oui, mais une autre réponse montre que c'est environ 5 fois plus lent que Array(n).fill(0)dans Chrome si ce dont vous avez vraiment besoin est un tableau JS. Si vous pouvez utiliser un TypedArray, cela est même beaucoup plus rapide que .fill(0), surtout si vous pouvez utiliser la valeur d'initialisation par défaut de 0. Il ne semble pas y avoir de constructeur qui accepte une valeur de remplissage et une longueur, comme le fait C ++ std::vector. Il semble que pour toute valeur non nulle, vous devez construire un TypedArray mis à zéro, puis le remplir. : /
Peter Cordes
29

Si vous utilisez ES6, vous pouvez utiliser Array.from () comme ceci:

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

A le même résultat que

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

Parce que

Array.from({ length: 3 })
//[undefined, undefined, undefined]
foxiris
la source
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']

Notez que whileest généralement plus efficace que for-in, forEachetc.

kangax
la source
3
La ivariable locale n'est-elle pas étrangère? lengthest passé par valeur, vous devriez donc pouvoir le décrémenter directement.
Sean Bright
3
Bien que cela ait l'air bien au premier abord, il est malheureusement très lent d'attribuer des valeurs à un point arbitraire dans un arary (par exemple arr[i] = value). Il est beaucoup plus rapide de parcourir du début à la fin et de l'utiliser arr.push(value). C'est ennuyeux, car je préfère ta méthode.
Nick Brunt
19

en utilisant la notation d'objet

var x = [];

zéro rempli? comme...

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

rempli de 'indéfini' ...

var x = new Array(7);

notation obj avec des zéros

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

En remarque, si vous modifiez le prototype d'Array, les deux

var x = new Array();

et

var y = [];

aura ces modifications de prototype

En tout cas, je ne serais pas trop préoccupé par l'efficacité ou la vitesse de cette opération, il y a beaucoup d'autres choses que vous ferez probablement qui sont beaucoup plus inutiles et coûteuses que l'instanciation d'un tableau de longueur arbitraire contenant des zéros.

Allen Rice
la source
5
Euh ... il n'y a pas de nulls dans ce tableau -var x = new Array(7);
kangax
5
En fait, le tableau ne se remplit de rien avec de nouveaux tableaux (n), pas même de `` non définis '', il définit simplement la valeur de la longueur des tableaux à n. Vous pouvez le vérifier en appelant (new Array (1)). ForEach (...). forEach ne s'exécute jamais, contrairement à si vous l'appelez sur [undefined].
JussiR
4
new Array(7)ne crée pas un tableau "rempli de non défini". Il crée un tableau vide de longueur 7.
RobG
1
Vous voudrez peut-être reconsidérer certaines parties de votre réponse, car ce que @RobG dit est critique (si ce que vous disiez était vrai, le mappage aurait été beaucoup plus facile)
Abdo
1
Ces jours-ci, vous pourriez le faire (new Array(10)).fill(0).
Javier de la Rosa
18

J'ai testé toutes les combinaisons de boucles de pré-allocation / non pré-allocation, de comptage / décroissement et pour / pendant dans IE 6/7/8, Firefox 3.5, Chrome et Opera.

Les fonctions ci-dessous étaient systématiquement les plus rapides ou extrêmement proches dans Firefox, Chrome et IE8, et pas beaucoup plus lentes que les plus rapides dans Opera et IE 6. C'est aussi la plus simple et la plus claire à mon avis. J'ai trouvé plusieurs navigateurs où la version de la boucle while est légèrement plus rapide, donc je l'inclus aussi pour référence.

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

ou

function newFilledArray(length, val) {
    var array = [];
    var i = 0;
    while (i < length) {
        array[i++] = val;
    }
    return array;
}
Matthew Crumley
la source
1
Vous pouvez également lancer la var array = []déclaration dans la première partie de la boucle for, séparée par une virgule uniquement.
damianb
J'aime cette suggestion de damianb, mais n'oubliez pas de mettre l'affectation et la virgule avant l'incrémentation! `for (var i = 0; i <length; array [i] = val, i ++);
punstress
Faites ce que tout le monde manque à votre deuxième et définissez la longueur du tableau sur la lengthvaleur déjà donnée afin qu'il ne change pas constamment. Apporté un tableau de 1 million de longueur de zéro de 40 ms à 8 sur ma machine.
Jonathan Gray
Je semble obtenir une augmentation de 10 à 15% de la vitesse lorsque je refaçonne cette solution en un revêtement. for (i = 0, array = []; i < length; ++i) array[i] = val;.. Moins de blocs? ... de toute façon, aussi ... si je règle la array.lengthlongueur du nouveau tableau .. il me semble que j'obtiens une autre augmentation de vitesse de 10% à 15% dans FF ... dans Chrome, il semble doubler la vitesse -> var i, array = []; array.length = length; while(i < length) array[i++] = val;(était encore plus rapide si je forle whilelaissais en boucle ... mais l'init n'est plus nécessaire, donc c'est apparemment plus rapide sur cette version)
Pimp Trizkit
Je le noterai également lors de mes tests. Dans un nombre décent de mes cas de test, la version finale ci-dessus semble fonctionner de 3 fois à bien plus de 10 fois plus vite ... Je ne sais pas trop pourquoi ... (différentes tailles de tableau testées entre chrome et FF)
Pimp Trizkit
13

Si vous devez créer de nombreux tableaux remplis de zéro de longueurs différentes pendant l'exécution de votre code, le moyen le plus rapide que j'ai trouvé pour y parvenir est de créer une fois un tableau zéro , en utilisant l'une des méthodes mentionnées sur ce sujet, d'une longueur que vous savez ne sera jamais dépassé, puis découpez ce tableau si nécessaire.

Par exemple (en utilisant la fonction de la réponse choisie ci-dessus pour initialiser le tableau), créez un tableau rempli de zéro de longueur maxLength , comme une variable visible par le code qui a besoin de zéro tableaux:

var zero = newFilledArray(maxLength, 0);

Tranchez maintenant ce tableau à chaque fois que vous avez besoin d'un tableau rempli de zéro de longueur requiredLength < maxLength :

zero.slice(0, requiredLength);

Je créais des tableaux remplis de zéro des milliers de fois pendant l'exécution de mon code, ce qui a considérablement accéléré le processus.

Nenad Vukicevic
la source
13
function zeroFilledArray(size) {
    return new Array(size + 1).join('0').split('');
}
Eli
la source
3
Vous pouvez également utiliser new Array(size+1).join("x").split("x").map(function() { return 0; })pour obtenir des chiffres réels
Yuval
6
@Yuval Ou tout simplementnew Array(size+1).join('0').split('').map(Number)
Paul
11

Je n'ai rien contre:

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

suggéré par Zertosh, mais dans un nouveau tableau ES6 , les extensions vous permettent de le faire en natif avec la fillméthode. Maintenant, IE edge, Chrome et FF le prennent en charge, mais vérifiez le tableau de compatibilité

new Array(3).fill(0)vous donnera [0, 0, 0]. Vous pouvez remplir le tableau avec n'importe quelle valeur comme new Array(5).fill('abc')(même des objets et d'autres tableaux).

En plus de cela, vous pouvez modifier les tableaux précédents avec fill:

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

ce qui vous donne: [1, 2, 3, 9, 9, 6]

Salvador Dali
la source
10

La façon dont je le fais habituellement (et c'est incroyablement rapide) utilise Uint8Array. Par exemple, créer un vecteur rempli de zéro d'éléments 1M:

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

Je suis un utilisateur Linux et j'ai toujours travaillé pour moi, mais une fois qu'un ami utilisant un Mac avait des éléments différents de zéro. Je pensais que sa machine fonctionnait mal, mais voici toujours le moyen le plus sûr que nous avons trouvé pour le réparer:

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

Édité

Chrome 25.0.1364.160

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

Firefox 20.0

  1. Allen Rice - 1,85
  2. Josué - 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 - ERREUR

Il manque le test le plus important (du moins pour moi): celui de Node.js. Je le soupçonne proche du benchmark Chrome.

dur
la source
C'est le moyen le plus efficace pour mes doigts et pour mes yeux. Mais c'est très très lent pour Chrome (selon ce jsperf. 99% plus lent).
Orwellophile
1
Je me demande si le problème sur le Mac de votre ami était lié à: stackoverflow.com/questions/39129200/… ou peut-être que Array.slice ne gérait pas UInt8Array et fuyait de la mémoire non initialisée? (un problème de sécurité!).
robocat
@robocat Bonne prise! Si je me souviens bien, nous utilisions Node.js 0,6 ou 0,8. Nous avons pensé à une sorte de fuite, mais nous ne pouvions pas la reproduire avec la pile de production, nous avons donc décidé de l'ignorer.
dur
10

Utiliser lodash ou underscore

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

Ou si vous avez un tableau existant et que vous voulez un tableau de la même longueur

array.map(_.constant(0));
Djechlin
la source
Je suis tellement content que vous ayez ajouté cette réponse, car j'utilise le soulignement, et je savais qu'il y avait quelque chose pour cela ... mais je n'avais pas encore pu le trouver. Je souhaite juste pouvoir créer des tableaux d'objets en utilisant ceci
PandaWood
@PandaWood _.range (0, longueur -1, 0) .map (Object.new), je pense.
djechlin
Devrait l'être _.range(0, length, 0), je crois. Lodash est exclusif de la valeur
finale
9

Solution ES6:

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

Depuis ECMAScript2016 , il y a un choix clair pour les grands tableaux.

Étant donné que cette réponse apparaît toujours en haut sur les recherches Google, voici une réponse pour 2017.

Voici un jsbench actuel avec quelques dizaines de méthodes populaires, dont beaucoup ont été proposées jusqu'à présent sur cette question. Si vous trouvez une meilleure méthode, veuillez ajouter, bifurquer et partager.

Je veux noter qu'il n'y a pas de véritable moyen le plus efficace de créer un tableau rempli de longueur arbitraire. Vous pouvez optimiser la vitesse ou la clarté et la maintenabilité - l'un ou l'autre peut être considéré comme le choix le plus efficace en fonction des besoins du projet.

Lors de l'optimisation de la vitesse, vous souhaitez: créer le tableau à l'aide de la syntaxe littérale; définissez la longueur, initialisez la variable d'itération et parcourez le tableau à l'aide d'une boucle while. Voici un exemple.

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

Une autre mise en œuvre possible serait:

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

Mais je déconseille fortement d'utiliser cette deuxième implantation dans la pratique car elle est moins claire et ne vous permet pas de maintenir la portée du bloc sur votre variable de tableau.

Celles-ci sont nettement plus rapides que le remplissage avec une boucle for, et environ 90% plus rapides que la méthode standard de

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

Mais cette méthode de remplissage reste le choix le plus efficace pour les petits tableaux en raison de sa clarté, de sa concision et de sa maintenabilité. La différence de performances ne vous tuera probablement pas à moins que vous ne fabriquiez beaucoup de tableaux avec des longueurs de l'ordre de milliers ou plus.

Quelques autres notes importantes. La plupart des guides de style vous recommandent de ne plus utiliser varsans raison très spéciale lors de l'utilisation d'ES6 ou d'une version ultérieure. À utiliser constpour les variables qui ne seront pas redéfinies et letpour les variables qui le seront. Le MDN et le Airbnb's Style Guide sont d'excellents endroits où aller pour plus d'informations sur les meilleures pratiques. Les questions ne portaient pas sur la syntaxe, mais il est important que les nouveaux utilisateurs de JS connaissent ces nouvelles normes lorsqu'ils recherchent parmi ces rames d'anciennes et de nouvelles réponses.

Isaac B
la source
8

Pour créer un tout nouveau tableau

new Array(arrayLength).fill(0);

Pour ajouter des valeurs à la fin d'un tableau existant

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

Exemple

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

Juanma Menendez
la source
6

Je n'ai pas vu cette méthode dans les réponses, alors voici:

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

En conséquence, vous obtiendrez un tableau de valeur nulle de longueur 200:

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

Je ne suis pas sûr des performances de ce code, mais cela ne devrait pas être un problème si vous l'utilisez pour des tableaux relativement petits.

Eugene Tiurin
la source
5
Ni le plus rapide ni le plus court mais une belle contribution à la diversité des solutions.
7vujy0f0hy
5

const arr = Array.from({ length: 10 }).fill(0)

alex dykyі
la source
1
Cela ne remplit pas le tableau avec des zéros. Il le remplit d'indéfini.
jlh
@jlh thanks fixed
alex dykyі
4

Cette concatversion est beaucoup plus rapide dans mes tests sur Chrome (2013-03-21). Environ 200 ms pour 10 000 000 d'éléments contre 675 pour une init droite.

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: si vous voulez remplir votre tableau avec des chaînes, c'est une façon concise de le faire (pas aussi vite que concatsi):

function filledArrayString(len, value) {
    return new Array(len+1).join(value).split('');
}
Sam Barnum
la source
2
Ok, sauvage. C'est bien plus rapide que d'utiliser un nouveau tableau (len). MAIS! Je constate dans Chrome que les lectures ultérieures de ces données prennent beaucoup plus de temps. Voici quelques horodatages pour montrer ce que je veux dire: (Utilisation d'un nouveau tableau (len)) 0.365: Création d'un tableau 4.526: Exécution d'une convolution 10.75: Convolution terminée (Utilisation d'un concat) 0.339: Création d'un tableau 0.591: Exécution d'une convolution // OMG, WAY plus rapide 18.056: Convolution terminée
Brooks
4

Je testais la grande réponse de TJ Crowder, et j'ai proposé une fusion récursive basée sur la solution concat qui surpasse n'importe laquelle dans ses tests dans Chrome (je n'ai pas testé d'autres navigateurs).

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

appelez la méthode avec makeRec(29).

Frederik Gottlieb
la source
4

Et alors new Array(51).join('0').split('')?

Cory Mawhorter
la source
1
alors .map(function(a){return +a})?
lonewarrior556
4

Il convient peut-être de le signaler, qui Array.prototype.fillavait été ajouté dans le cadre de la proposition ECMAScript 6 (Harmony) . Je préfère aller avec le polyfill écrit ci-dessous, avant d'envisager d'autres options mentionnées sur le fil.

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;
  };
}
Ivo
la source
4

Le plus court pour le code de boucle

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;

Version var sûre

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

edit:
for(var i=100,a=[];i--;)a[i]=0;
nathnolt
la source
2
Étant donné que la longueur est une variable définie n, elle serait plus courte:for(var a=[];n--;a[n]=0);
Tomas Langkaas
4

let filled = [];
filled.length = 10;
filled.fill(0);

console.log(filled);

Tomiwa Adefokun
la source
3

Ma fonction la plus rapide serait:

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'utilisation du push and shift natif pour ajouter des éléments au tableau est beaucoup plus rapide (environ 10 fois) que de déclarer la portée du tableau et de référencer chaque élément pour définir sa valeur.

fyi: J'obtiens constamment des temps plus rapides avec la première boucle, qui compte à rebours, lorsque je l'exécute dans firebug (extension 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

J'aimerais savoir ce que TJ Crowder en fait? :-)

Joshua
la source
Vous pouvez le rendre plus rapide en le changeant en while (len--).. a pris mes temps de traitement d'environ
60 ms
La réponse de Matthew Crumbly bat toujours (30 ms)!
nickf
3

Je savais que j'avais ce proto quelque part :)

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

Edit: tests

En réponse à Joshua et à d'autres méthodes, j'ai effectué mon propre benchmarking et je vois des résultats complètement différents de ceux rapportés.

Voici ce que j'ai testé:

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

Résultats:

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

Donc, à mon avis, la poussée est en effet plus lente en général, mais fonctionne mieux avec des tableaux plus longs dans FF mais pire dans IE, ce qui est nul en général (quelle surprise).

annakata
la source
Je viens de le tester: la deuxième méthode ( b = []...) est 10 à 15% plus rapide que la première, mais elle est plus de 10 fois plus lente que la réponse de Joshua.
nickf
Je sais que c'est un ancien poste . Mais peut-être que ça intéresse toujours les autres (comme moi). Par conséquent, je voudrais suggérer une addition à la fonction prototype: inclure un else {this.length=n;}après la this.length-check. Cela raccourcira un tableau déjà existant si nécessaire lors de sa initrecialisation à une longueur différente n.
cars10m
2

Fonction anonyme:

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

Un peu plus court avec for-loop:

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

Fonctionne avec tout Object, changez simplement ce qu'il y a à l'intérieurthis.push() .

Vous pouvez même enregistrer la fonction:

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

Appelez-le en utilisant:

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

Ajout d'éléments à un tableau déjà existant:

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

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

Mateo Gianolio
la source