1544 votes

Comment créer un tableau contenant 1...N

Je cherche des alternatives à la méthode ci-dessous pour créer un tableau JavaScript contenant de 1 à N, où N n'est connu qu'au moment de l'exécution.

var foo = [];

for (var i = 1; i <= N; i++) {
   foo.push(i);
}

J'ai l'impression qu'il devrait y avoir un moyen de faire cela sans la boucle.

297 votes

Après avoir lu toute cette page, je suis arrivé à la conclusion que votre propre boucle for est la plus simple, la plus lisible et la moins sujette aux erreurs.

0 votes

Si quelqu'un a besoin de quelque chose de plus avancé, j'ai créé une librairie node.js qui fait cela pour les chiffres, les lettres, les plages négatives/positives, etc. github.com/jonschlinkert/fill-range . Il est utilisé dans github.com/jonschlinkert/braces pour l'expansion de l'accolade et github.com/jonschlinkert/micromatch pour les motifs globaux

0 votes

Une autre façon de procéder peut être la suivante : Array.from({length : 10}, (_, v) => v)

861voto

Igor Shubin Points 1568

Vous pouvez le faire :

var N = 10; 
Array.apply(null, {length: N}).map(Number.call, Number)

résultat : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].

ou avec des valeurs aléatoires :

Array.apply(null, {length: N}).map(Function.call, Math.random)

résultat : [0.7082694901619107, 0.9572225909214467, 0.8586748542729765, 0.8653848143294454, 0.008339877473190427, 0.9911756622605026, 0.8133423360995948, 0.8377588465809822, 0.5577575915958732, 0.16363654541783035]

Explication

Tout d'abord, notez que Number.call(undefined, N) est équivalent à Number(N) qui renvoie simplement N . Nous utiliserons ce fait plus tard.

Array.apply(null, [undefined, undefined, undefined]) est équivalent à Array(undefined, undefined, undefined) qui produit un tableau de trois éléments et assigne undefined à chaque élément.

Comment pouvez-vous généraliser cela à N éléments ? Examinez comment Array() fonctionne, ce qui donne quelque chose comme ça :

function Array() {
    if ( arguments.length == 1 &&
         'number' === typeof arguments[0] &&
         arguments[0] >= 0 && arguments &&
         arguments[0] < 1 << 32 ) {
        return [ … ];  // array of length arguments[0], generated by native code
    }
    var a = [];
    for (var i = 0; i < arguments.length; i++) {
        a.push(arguments[i]);
    }
    return a;
}

Depuis ECMAScript 5 , Function.prototype.apply(thisArg, argsArray) accepte également un objet de type tableau de type canard comme second paramètre. Si nous invoquons Array.apply(null, { length: N }) alors il exécutera

function Array() {
    var a = [];
    for (var i = 0; i < /* arguments.length = */ N; i++) {
        a.push(/* arguments[i] = */ undefined);
    }
    return a;
}

Maintenant, nous avons un N -dont chaque élément est défini comme suit undefined . Lorsque nous appelons .map(callback, thisArg) sur elle, chaque élément sera défini comme le résultat de callback.call(thisArg, element, index, array) . Par conséquent, [undefined, undefined, …, undefined].map(Number.call, Number) mettrait chaque élément en correspondance avec (Number.call).call(Number, undefined, index, array) qui est identique à Number.call(undefined, index, array) qui, comme nous l'avons observé plus tôt, est égal à index . Cela complète le tableau dont les éléments sont identiques à leur indice.

Pourquoi se donner la peine de Array.apply(null, {length: N}) au lieu de simplement Array(N) ? Après tout, les deux expressions donneraient lieu à un N -élément tableau d'éléments non définis. La différence est que dans la première expression, chaque élément est explicitement set à indéfini, alors que dans le second, chaque élément n'a jamais été défini. Selon la documentation de .map() :

callback n'est invoqué que pour les index du tableau qui ont des valeurs assignées ; il n'est pas invoqué pour les index qui ont été supprimés ou qui n'ont jamais été assignés.

Par conséquent, Array(N) est insuffisante ; Array(N).map(Number.call, Number) résulterait en un tableau non initialisé de longueur N .

Compatibilité

Comme cette technique repose sur le comportement de Function.prototype.apply() spécifié dans ECMAScript 5, il sera ne fonctionne pas dans les navigateurs pré-ECMAScript 5 tels que Chrome 14 et Internet Explorer 9.

65 votes

+1 pour l'ingéniosité mais notez que c'est plusieurs fois plus LENT qu'une boucle for primitive : jsperf.com/array-magic-vs-for

8 votes

Très intelligent probablement trop. Exploiter le fait que Function.prototype.call Le premier paramètre de l'utilisateur est le this pour le mettre directement en correspondance avec Array.prototype.map Le paramètre iterator de l'utilisateur a un certain éclat.

16 votes

C'est vraiment, vraiment intelligent (à la limite de l'abus de JS). L'idée vraiment importante ici est l'idiosyncrasie de map sur des valeurs non attribuées, à mon avis. Une autre version (et peut-être un peu plus claire, bien que plus longue) est : Array.apply(null, { length: N }).map(function(element, index) { return index; })

500voto

scunliffe Points 30964

Si je comprends ce que tu veux, tu veux un tableau de chiffres. 1..n que vous pouvez ensuite parcourir en boucle.

Si c'est tout ce dont vous avez besoin, pouvez-vous faire ceci à la place ?

var foo = new Array(45); // create an empty array with length 45

puis quand vous voulez l'utiliser... (non optimisé, juste pour l'exemple)

for(var i = 0; i < foo.length; i++){
  document.write('Item: ' + (i + 1) + ' of ' + foo.length + '<br/>'); 
}

par exemple, si vous n'avez pas besoin de magasin n'importe quoi dans le tableau, vous avez juste besoin d'un conteneur de la bonne longueur sur lequel vous pouvez itérer... cela pourrait être plus facile.

Voyez-le en action ici : http://jsfiddle.net/3kcvm/

5 votes

Impressionné que vous ayez réussi à formuler ma question mieux que je ne l'aurais fait, vous avez en effet raison car, à la réflexion, tout ce dont j'ai besoin est un tableau de nombres que je peux ensuite parcourir en boucle :) Merci pour votre réponse.

179 votes

@Godders : Si c'est ce que vous cherchez, pourquoi avez-vous besoin d'un tableau ? Un simple var n = 45; puis en bouclant à partir de 1..n ferait.

2 votes

@casablanca : bien sûr que vous avez raison, je n'arrive pas à croire que je n'ai pas pu trouver la réponse simple avant de poster la question. Mais bon, j'ai fini par y arriver. Merci !

120voto

Evan Points 1850

Utilisez le très populaire Méthode de soulignement _.range

// _.range([start], stop, [step])

_.range(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11); // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5); // => [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1); //  => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0); // => []

7 votes

0 votes

Génial. Montrez-moi un grand projet qui n'utilise pas l'underscore de toute façon...

73voto

Ian Henry Points 9239
function range(start, end) {
    var foo = [];
    for (var i = start; i <= end; i++) {
        foo.push(i);
    }
    return foo;
}

Alors appelé par

var foo = range(1, 5);

Il n'y a pas de moyen intégré de faire cela en Javascript, mais c'est une fonction utilitaire parfaitement valable à créer si vous avez besoin de le faire plus d'une fois.

Edit : A mon avis, ce qui suit est une meilleure fonction de gamme. Peut-être juste parce que je suis biaisé par LINQ, mais je pense qu'elle est plus utile dans plus de cas. Votre avis peut varier.

function range(start, count) {
    if(arguments.length == 1) {
        count = start;
        start = 0;
    }

    var foo = [];
    for (var i = 0; i < count; i++) {
        foo.push(start + i);
    }
    return foo;
}

2 votes

J'aime ça. Si vous vouliez aller plus loin, vous pourriez le déclarer comme Array.prototype.range = function(start, end) { ... } ;. Ensuite, vous pouvez appeler range(x, y) sur n'importe quel objet Array.

9 votes

Il s'agit plutôt d'une méthode de Array au lieu de Array.prototype car il n'y a aucune raison (cela pourrait même être considéré comme plutôt stupide) d'avoir cette méthode sur chaque tableau.

9 votes

Array.range(1, 5) serait probablement plus approprié, mais il y a quelque chose de cool à écrire [].range(1, 5) .

42voto

Tyler Rick Points 3033

Si vous utilisez d3.js dans votre application comme je le fais, D3 fournit une fonction d'aide qui le fait pour vous.

Donc pour obtenir un tableau de 0 à 4, c'est aussi simple que ça :

d3.range(5)
[0, 1, 2, 3, 4]

et pour obtenir un tableau de 1 à 5, comme vous le demandiez :

d3.range(1, 5+1)
[1, 2, 3, 4, 5]

Vérifiez ce tutoriel pour plus d'informations.

0 votes

Ce commentaire m'a donné l'idée de chercher la fonction range() dans RamdaJS, qui se trouve être la bibliothèque JS avec laquelle je travaille sur mon projet actuel. Parfait.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X