JavaScript a-t-il une méthode comme «range ()» pour générer une plage dans les limites fournies?

874

En PHP, vous pouvez faire ...

range(1, 3); // Array(1, 2, 3)
range("A", "C"); // Array("A", "B", "C")

Autrement dit, il existe une fonction qui vous permet d'obtenir une plage de chiffres ou de caractères en passant les limites supérieure et inférieure.

Y a-t-il quelque chose de natif intégré à JavaScript pour cela? Sinon, comment pourrais-je le mettre en œuvre?

alex
la source
1
Prototype.js a la $Rfonction, mais à part ça, je ne le pense pas vraiment.
Yi Jiang
Cette question (connexe) a d'excellentes réponses: stackoverflow.com/questions/6299500/…
btk
Array.from("ABC") //['A', 'B', 'C']C'est la chose la plus proche que je puisse trouver pour la deuxième partie de la question.
Andrew_1510
@ Andrew_1510 Vous pouvez split("")également y utiliser
alex
1
Lorsque l'amant lié est nul, cet oneliner:Array.apply(null, { length: 10 }).map(eval.call, Number)
csharpfolk

Réponses:

1503

Nombres

[...Array(5).keys()];
 => [0, 1, 2, 3, 4]

Itération de caractère

String.fromCharCode(...[...Array('D'.charCodeAt(0) - 'A'.charCodeAt(0) + 1).keys()].map(i => i + 'A'.charCodeAt(0)));
 => "ABCD"

Itération

for (const x of Array(5).keys()) {
  console.log(x, String.fromCharCode('A'.charCodeAt(0) + x));
}
 => 0,"A" 1,"B" 2,"C" 3,"D" 4,"E"

Comme fonctions

function range(size, startAt = 0) {
    return [...Array(size).keys()].map(i => i + startAt);
}

function characterRange(startChar, endChar) {
    return String.fromCharCode(...range(endChar.charCodeAt(0) -
            startChar.charCodeAt(0), startChar.charCodeAt(0)))
}

En tant que fonctions tapées

function range(size:number, startAt:number = 0):ReadonlyArray<number> {
    return [...Array(size).keys()].map(i => i + startAt);
}

function characterRange(startChar:string, endChar:string):ReadonlyArray<string> {
    return String.fromCharCode(...range(endChar.charCodeAt(0) -
            startChar.charCodeAt(0), startChar.charCodeAt(0)))
}

_.range()Fonction lodash.js

_.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]
String.fromCharCode(..._.range('A'.charCodeAt(0), 'D'.charCodeAt(0) + 1));
 => "ABCD"

Anciens navigateurs non es6 sans bibliothèque:

Array.apply(null, Array(5)).map(function (_, i) {return i;});
 => [0, 1, 2, 3, 4]

console.log([...Array(5).keys()]);

(Crédit ES6 à Nils Petersohn et à d'autres commentateurs)

Ben
la source
72
Parce que s'il est utile n'importe où, il est probablement utile dans JS. (JS peut faire des choses de type programmation fonctionnelle, qui peuvent bénéficier d'une plage (instruction 0. Cela et mille autres raisons, cela pourrait être utile dans certains cas semi-rares)
Lodewijk
5
Une idée pourquoi simplement utiliser (new Array(5)).map(function (value, index) { return index; })ne fonctionnerait pas? Cela revient [undefined × 5]pour moi dans Chrome DevTools.
Lewis
12
@Lewis Parce qu'un tableau défini avec qui a des emplacements vides qui ne seront pas répétés avec map()ou l'un de ses amis.
alex
65
Array.from (Array (5) .keys ())
nils petersohn
17
Array(5).fill()est également mappable
nils petersohn
333

Pour les nombres, vous pouvez utiliser ES6 Array.from(), qui fonctionne dans tout, de nos jours, sauf IE:

Version plus courte:

Array.from({length: 20}, (x,i) => i);

Version plus longue:

Array.from(new Array(20), (x,i) => i)

ce qui crée un tableau de 0 à 19 inclus. Cela peut être encore raccourci à l'une de ces formes:

Array.from(Array(20).keys())
// or
[...Array(20).keys()]

Des limites inférieures et supérieures peuvent également être spécifiées, par exemple:

Array.from(new Array(20), (x,i) => i + *lowerBound*)

Un article décrivant cela plus en détail: http://www.2ality.com/2014/05/es6-array-methods.html

Kristjan Liiva
la source
50
Le premier exemple peut même être simplifié en [... Array (20) .keys ()]
Delapouite
27
Légèrement plus succinct que la Array.from()méthode et plus rapide que les deux:Array(20).fill().map((_, i) => i)
Stu Cox
2
@Delapouite Awesome! Vous devriez en faire une réponse distincte, et je voterai pour! C'est aussi la réponse parfaite à ce doublon .
foc
9
@Delapouite @jib Et cela aussi:Array.from({length: end - start}, (v, k) => k + start)
Aditya Singh
1
@ icc97 Oui, les linters peuvent se plaindre, bien qu'en JavaScript, omettre un argument de fonction défini comme étant identique à la transmission undefined, donc fill()(sans argument) n'est pas faux en soi. La valeur de remplissage n'est pas utilisée dans cette solution, donc si vous le souhaitez, vous pouvez utiliser fill(0)pour enregistrer quelques caractères.
Stu Cox
122

Ma nouvelle forme préférée ( ES2015 )

Array(10).fill(1).map((x, y) => x + y)

Et si vous avez besoin d'une fonction avec un stepparamètre:

const range = (start, stop, step = 1) =>
  Array(Math.ceil((stop - start) / step)).fill(start).map((x, y) => x + y * step)
Kutyel
la source
5
let range = (start, stop, step = 1) => Array (stop - start) .fill (start) .map ((x, y) => x + y * step)
rodfersou
4
@rodfersou FYI: votre exemple est faux. stopn'est pas réellement la position d'arrêt / fin mais le compte / la distance. (pas d'offense, juste pour sensibiliser les gens à la faute de frappe)
F Lekschas
4
Pour les confus - en raison de l'édition de rodfersou après le commentaire de F Lekschas, son code est maintenant correct.
eedrah
1
L'argument dans lequel vous passez Array(Math.ceil((stop - start) / step) + 1), a besoin +1de la fin, pour vraiment imiter le comportement "inclusif" de php.
Johan Dettmar
3
C'est la première réponse qui répond en fait à la question complète d'une fonction Javascript qui implémente pleinement une rangeméthode. Tous les autres actuellement au-dessus de cela (à l'exception de lodash _.range) implémentent des itérateurs de base plutôt qu'une fonction de plage réelle avec démarrage, arrêt et étape
icc97
99

Voici mes 2 cents:

function range(start, count) {
  return Array.apply(0, Array(count))
    .map((element, index) => index + start);
}
jflood.net
la source
1
Excellente utilisation des fonctions de haut niveau.
Farzad YZ
5
C'est en fait faux parce que la question demande des valeurs de début et de fin. Pas de départ et de comptage / distance.
James Robey
73

Il fonctionne pour les caractères et les chiffres, en avant ou en arrière avec une étape facultative.

var range = function(start, end, step) {
    var range = [];
    var typeofStart = typeof start;
    var typeofEnd = typeof end;

    if (step === 0) {
        throw TypeError("Step cannot be zero.");
    }

    if (typeofStart == "undefined" || typeofEnd == "undefined") {
        throw TypeError("Must pass start and end arguments.");
    } else if (typeofStart != typeofEnd) {
        throw TypeError("Start and end arguments must be of same type.");
    }

    typeof step == "undefined" && (step = 1);

    if (end < start) {
        step = -step;
    }

    if (typeofStart == "number") {

        while (step > 0 ? end >= start : end <= start) {
            range.push(start);
            start += step;
        }

    } else if (typeofStart == "string") {

        if (start.length != 1 || end.length != 1) {
            throw TypeError("Only strings with one character are supported.");
        }

        start = start.charCodeAt(0);
        end = end.charCodeAt(0);

        while (step > 0 ? end >= start : end <= start) {
            range.push(String.fromCharCode(start));
            start += step;
        }

    } else {
        throw TypeError("Only string and number types are supported");
    }

    return range;

}

jsFiddle .

Si vous préférez augmenter les types natifs, affectez-le à Array.range.

alex
la source
53

Fonction de portée simple:

function range(start, stop, step) {
    var a = [start], b = start;
    while (b < stop) {
        a.push(b += step || 1);
    }
    return a;
}

Pour incorporer le type de données BitInt, une vérification peut être incluse, garantissant que toutes les variables sont identiques typeof start:

function range(start, stop, step) {
    var a = [start], b = start;
    if (typeof start == 'bigint') {
        stop = BigInt(stop)
        step = step? BigInt(step): 1n;
    } else
        step = step || 1;
    while (b < stop) {
        a.push(b += step);
    }
    return a;
}

Pour supprimer des valeurs supérieures à celles définies par stopexemple range(0,5,2), inclura 6, qui ne devrait pas l'être.

function range(start, stop, step) {
    var a = [start], b = start;
    while (b < stop) {
        a.push(b += step || 1);
    }
    return (b > stop) ? a.slice(0,-1) : a;
}
Rémi
la source
3
PLUS UNO pour utilisable et lisible. Meilleur extrait de code que j'ai vu depuis longtemps.
monsto
1
Cela ne fonctionne pas lorsque step != 1la whilecondition doit être prise stepen compte. Ma version mise à jour avec une stepvaleur par défaut : plage de fonctions (démarrage, arrêt, étape) {étape = étape || 1 var a = [début], b = début; while ((étape b +) <stop) {console.log ("b:" + b + ". a:" + a + "."); b + = pas; a. pousser (b); } retourne un; }
daveharris
@daveharris J'ai ajouté une étape par défaut ci - dessus, (step || 1).
M. Polywhirl
36
Array.range= function(a, b, step){
    var A= [];
    if(typeof a== 'number'){
        A[0]= a;
        step= step || 1;
        while(a+step<= b){
            A[A.length]= a+= step;
        }
    }
    else{
        var s= 'abcdefghijklmnopqrstuvwxyz';
        if(a=== a.toUpperCase()){
            b=b.toUpperCase();
            s= s.toUpperCase();
        }
        s= s.substring(s.indexOf(a), s.indexOf(b)+ 1);
        A= s.split('');        
    }
    return A;
}


    Array.range(0,10);
    // [0,1,2,3,4,5,6,7,8,9,10]

    Array.range(-100,100,20);
    // [-100,-80,-60,-40,-20,0,20,40,60,80,100]

    Array.range('A','F');
    // ['A','B','C','D','E','F')

    Array.range('m','r');
    // ['m','n','o','p','q','r']
kennebec
la source
Vous ne devriez vraiment pas jerry-rig méthodes sur le Arrayprototype.
connectyourcharger
Cette méthode ne fonctionne qu'avec des entiers et des caractères. Si les paramètres sont nuls, undefined NaN, booléen, tableau, objet, etc, cette méthode renvoie l'erreur suivante: undefined method toUpperCase to etc!
Victor
`` if (typeof from! == 'number' && typeof from! == 'string') {throw new TypeError ('Le premier paramètre doit être un nombre ou un caractère')} if (typeof to! == ' number '&& typeof to! ==' string ') {throw new TypeError (' The first parameter should be a number or a character ')} `` `
Victor
36

OK, en JavaScript, nous n'avons pas de range()fonction comme PHP , nous devons donc créer la fonction qui est assez simple, j'écris quelques fonctions d'une ligne pour vous et je les sépare pour les nombres et les alphabets comme ci-dessous:

pour les nombres :

function numberRange (start, end) {
  return new Array(end - start).fill().map((d, i) => i + start);
}

et appelez-le comme:

numberRange(5, 10); //[5, 6, 7, 8, 9]

pour les alphabets :

function alphabetRange (start, end) {
  return new Array(end.charCodeAt(0) - start.charCodeAt(0)).fill().map((d, i) => String.fromCharCode(i + start.charCodeAt(0)));
}

et appelez-le comme:

alphabetRange('c', 'h'); //["c", "d", "e", "f", "g"]
Alireza
la source
2
Je pense qu'il y a des erreurs ponctuelles dans ces fonctions. Devrait être Array(end - start + 1), et Array(end.charCodeAt(0) - start.charCodeAt(0) + 1).
earcanal
24

Handy fonction pour faire le tour, exécutez l'extrait de code ci - dessous

function range(start, end, step, offset) {
  
  var len = (Math.abs(end - start) + ((offset || 0) * 2)) / (step || 1) + 1;
  var direction = start < end ? 1 : -1;
  var startingPoint = start - (direction * (offset || 0));
  var stepSize = direction * (step || 1);
  
  return Array(len).fill(0).map(function(_, index) {
    return startingPoint + (stepSize * index);
  });
  
}

console.log('range(1, 5)=> ' + range(1, 5));
console.log('range(5, 1)=> ' + range(5, 1));
console.log('range(5, 5)=> ' + range(5, 5));
console.log('range(-5, 5)=> ' + range(-5, 5));
console.log('range(-10, 5, 5)=> ' + range(-10, 5, 5));
console.log('range(1, 5, 1, 2)=> ' + range(1, 5, 1, 2));

voici comment l'utiliser

plage (début, fin, étape = 1, décalage = 0);

  • inclus - avant range(5,10) // [5, 6, 7, 8, 9, 10]
  • inclus - en arrière range(10,5) // [10, 9, 8, 7, 6, 5]
  • reculer range(10,2,2) // [10, 8, 6, 4, 2]
  • exclusif - avant range(5,10,0,-1) // [6, 7, 8, 9] not 5,10 themselves
  • décalage - étendre range(5,10,0,1) // [4, 5, 6, 7, 8, 9, 10, 11]
  • offset - rétrécir range(5,10,0,-2) // [7, 8]
  • étape - élargir range(10,0,2,2) // [12, 10, 8, 6, 4, 2, 0, -2]

j'espère que vous le trouverez utile.


Et voici comment cela fonctionne.

Fondamentalement, je calcule d'abord la longueur du tableau résultant et crée un tableau rempli de zéro à cette longueur, puis le remplit avec les valeurs nécessaires

  • (step || 1)=> Et d'autres comme cela signifie utiliser la valeur de stepet si elle n'a pas été fournie, utiliser à la 1place
  • Nous commençons par calculer la longueur du tableau de résultats en utilisant (Math.abs(end - start) + ((offset || 0) * 2)) / (step || 1) + 1)pour le simplifier (différence * décalage dans les deux sens / pas)
  • Après avoir obtenu la longueur, nous créons un tableau vide avec des valeurs initialisées en utilisant la new Array(length).fill(0); vérification ici
  • Maintenant, nous avons un tableau [0,0,0,..]à la longueur souhaitée. Nous mappons dessus et retournons un nouveau tableau avec les valeurs dont nous avons besoin en utilisantArray.map(function() {})
  • var direction = start < end ? 1 : 0;Évidemment, si ce startn'est pas plus petit que le, endnous devons reculer. Je veux dire passer de 0 à 5 ou vice versa
  • À chaque itération, startingPoint+ stepSize* indexwill nous donne la valeur dont nous avons besoin
azerafati
la source
8
Pratique, très certainement. Facile? Je ne suis pas d'accord; peu importe que vous en fassiez une doublure. Venant de Python, c'est un choc.
PascalVKooten
@PascalvKooten, oui, bien sûr, cela aurait été génial s'il y avait une méthode intégrée pour cela, comme python, je suppose, mais c'était la plus simple que je pouvais trouver. Et cela s'est avéré utile dans mes projets.
azerafati
Publier un extrait de code douloureusement complexe comme celui-ci, en particulier sous la forme d'une seule ligne et sans explication sur son fonctionnement? Mauvais exemple d'une bonne réponse SO, qu'elle fonctionne ou non.
Madbreaks
1
@Madbreaks, oui, vous avez raison. J'ai été naïf d'en faire une doublure. je voulais juste donner à chacun une solution rapide et facile
azerafati
22
var range = (l,r) => new Array(r - l).fill().map((_,k) => k + l);
Klesun
la source
@nikkwong, le _est juste un nom d'argument dans le rappel de mappage. Vous savez, dans certaines langues, vous utiliseriez le _comme nom pour indiquer que la variable n'est pas utilisée.
Klesun
Ici cependant, _ne passe pas les arguments à range. Pourquoi pas?
nikk wong
2
Très propre! Cependant, il est important de noter que cela ne fonctionne pas sur IE ou Opera.
Rafael Xavier
4
Cette réponse doit être expliquée, car elle est peu adaptée au SO.
Madbreaks
@RafaelXavier travaillera sur IE avec Array.fill () polyfill
mwag
18

Utilisation des fonctions d' opérateur et de flèche Harmony :

var range = (start, end) => [...Array(end - start + 1)].map((_, i) => start + i);

Exemple:

range(10, 15);
[ 10, 11, 12, 13, 14, 15 ]
cPu1
la source
c'est la meilleure réponse!
Henry H.
1
Pas le plus rapide cependant.
mjwrazor
Que représente le symbole «_» dans ce cas?
Oleh Berehovskyi
@OlehBerehovskyi Cela signifie un paramètre de fonction lambda que vous n'avez pas l'intention d'utiliser réellement. Un linter qui met en garde contre les variables inutilisées doit l'ignorer.
Micah Zoltu
18

--- MISE À JOUR (Merci à @lokhmakov pour la simplification) ---

Une autre version utilisant des générateurs ES6 (voir la bonne réponse de Paolo Moretti avec les générateurs ES6 ):

const RANGE = (x,y) => Array.from((function*(){
  while (x <= y) yield x++;
})());

console.log(RANGE(3,7));  // [ 3, 4, 5, 6, 7 ]

Ou, si nous avons seulement besoin d'itérable, alors:

const RANGE_ITER = (x,y) => (function*(){
  while (x <= y) yield x++;
})();

for (let n of RANGE_ITER(3,7)){
  console.log(n);
}

// 3
// 4
// 5
// 6
// 7

--- Le code ORGINAL était: ---

const RANGE = (a,b) => Array.from((function*(x,y){
  while (x <= y) yield x++;
})(a,b));

et

const RANGE_ITER = (a,b) => (function*(x,y){
  while (x <= y) yield x++;
})(a,b);
Hero Qu
la source
1
Juste const range = (x, y) => Array.from(function* () { while (x <= y) yield x++; }())
lokhmakov
@lokhmakov Oui, vous avez raison. Merci! Je viens d'appliquer votre code dans ma réponse.
Hero Qu
15

A fait des recherches sur différentes fonctions de plage. Découvrez la comparaison jsperf des différentes façons de faire ces fonctions. Certainement pas une liste parfaite ou exhaustive, mais devrait aider :)

Le gagnant est...

function range(lowEnd,highEnd){
    var arr = [],
    c = highEnd - lowEnd + 1;
    while ( c-- ) {
        arr[c] = highEnd--
    }
    return arr;
}
range(0,31);

Techniquement, ce n'est pas le plus rapide sur Firefox, mais une différence de vitesse folle (à mon humble avis) sur Chrome le compense.

Une observation également intéressante est à quel point le chrome est plus rapide avec ces fonctions de tableau que Firefox. Chrome est au moins 4 ou 5 fois plus rapide .

Justin
la source
Notez que cela a été comparé aux fonctions de plage qui comprenaient un paramètre de taille de pas
binaryfunt
15

Le Javascript standard n'a pas de fonction intégrée pour générer des plages. Plusieurs frameworks javascript ajoutent la prise en charge de telles fonctionnalités, ou comme d'autres l'ont souligné, vous pouvez toujours lancer la vôtre.

Si vous souhaitez revérifier, la ressource définitive est la norme ECMA-262 .

Mike Dinescu
la source
Bien que je sois sûr d'une réponse parfaitement bonne en 2010, cela ne devrait plus être considéré comme la meilleure approche. Vous ne devez pas étendre les types intégrés, comme Prototype.js avait tendance à le faire 👍
Dana Woodman
@DanaWoodman merci d'avoir soulevé cette question - J'ai mis à jour la réponse pour supprimer la référence à Prototype.js car c'est en effet à peu près obsolète en 2018
Mike Dinescu
22
Eh bien, cela n'a pas aidé du tout.
Pithikos
@Pithikos Je vois que cette question a été modifiée depuis qu'elle a été posée à l'origine et que l'OP voulait savoir s'il existe une fonction de plage native dans JS.
Mike Dinescu
13

Vous pouvez utiliser lodash ou Undescore.js range :

var range = require('lodash/range')
range(10)
// -> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

Alternativement, si vous n'avez besoin que d'une plage consécutive d'entiers, vous pouvez faire quelque chose comme:

Array.apply(undefined, { length: 10 }).map(Number.call, Number)
// -> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

Dans ES6 rangepeut être implémenté avec des générateurs :

function* range(start=0, end=null, step=1) {
  if (end == null) {
    end = start;
    start = 0;
  }

  for (let i=start; i < end; i+=step) {
    yield i;
  }
}

Cette implémentation économise de la mémoire lors de l'itération de grandes séquences, car elle n'a pas à matérialiser toutes les valeurs dans un tableau:

for (let i of range(1, oneZillion)) {
  console.log(i);
}
Paolo Moretti
la source
La partie ES6 est maintenant la bonne réponse à cette question. Je recommanderais de supprimer les autres parties, qui sont couvertes par d'autres réponses.
joews
les générateurs sont quelque peu étranges s'ils sont utilisés en dehors d'une boucle: x = range (1, 10); // {} x; // {} // ressemble à une carte vide WTF!?! x.next (). value; // OK 1; x [3] // non défini, uniquement avec un tableau réel
Anona112
@ Anona112 vous pouvez utiliser Array.frompour convertir des générateurs en instances de tableau et inspecter la sortie.
Paolo Moretti
10

Un défi intéressant serait d'écrire la fonction la plus courte pour ce faire. Récursivité à la rescousse!

function r(a,b){return a>b?[]:[a].concat(r(++a,b))}

A tendance à être lent sur de grandes plages, mais heureusement, les ordinateurs quantiques sont juste autour du coin.

Un bonus supplémentaire est qu'il est obscur. Parce que nous savons tous combien il est important de cacher notre code aux regards indiscrets.

Pour vraiment et complètement obscurcir la fonction, procédez comme suit:

function r(a,b){return (a<b?[a,b].concat(r(++a,--b)):a>b?[]:[a]).sort(function(a,b){return a-b})}
Rick Hitchcock
la source
4
Court! = Simple, mais plus simple c'est mieux. Voici une version plus facile à lire const range = (a, b) => (a>=b) ? [] : [a, ...range(a+1, b)]:, en utilisant la syntaxe
ES6
1
@nafg: const range = (a, b, Δ = 1) => (a > b) ? [] : [a, ...range(a + Δ, b, Δ)];. Également en votant la réponse entière pour le commentaire.
7vujy0f0hy
10

Ce n'est peut-être pas la meilleure façon. Mais si vous cherchez à obtenir une plage de chiffres sur une seule ligne de code. Par exemple 10 - 50

Array(40).fill(undefined).map((n, i) => i + 10)

Où 40 est (fin - début) et 10 est le début. Cela devrait retourner [10, 11, ..., 50]

Edison D'souza
la source
9

Je coderais quelque chose comme ceci:

function range(start, end) {
    return Array(end-start).join(0).split(0).map(function(val, id) {return id+start});
}  

range(-4,2);
// [-4,-3,-2,-1,0,1]

range(3,9);
// [3,4,5,6,7,8]

Il se comporte de manière similaire à la gamme Python:

>>> range(-4,2)
[-4, -3, -2, -1, 0, 1]
Sasha Zezulinsky
la source
8

Une implémentation plutôt minimaliste qui utilise fortement ES6 peut être créée comme suit, en attirant une attention particulière sur la Array.from()méthode statique:

const getRange = (start, stop) => Array.from(
  new Array((stop - start) + 1),
  (_, i) => i + start
);
IsenrichO
la source
En remarque, j'ai créé un Gist dans lequel j'ai fait une getRange()sorte de fonction "améliorée" . En particulier, je visais à capturer les cas marginaux qui pourraient ne pas être traités dans la variante à nu ci-dessus. De plus, j'ai ajouté la prise en charge des plages alphanumériques. En d'autres termes, l'appeler avec deux entrées fournies comme 'C'et 'K'(dans cet ordre) renvoie un tableau dont les valeurs sont l'ensemble séquentiel de caractères de la lettre 'C' (inclus) à la lettre 'K' (exclusif):getRange('C', 'K'); // => ["C", "D", "E", "F", "G", "H", "I", "J"]
IsenrichO
vous n'avez pas besoin du newmot clé
Soldeplata Saketos
8

range(start,end,step): Avec les itérateurs ES6

Vous ne demandez que des limites supérieures et inférieures. Ici, nous en créons un avec une étape aussi.

Vous pouvez facilement créer une range()fonction de générateur qui peut fonctionner comme un itérateur. Cela signifie que vous n'avez pas à pré-générer l'intégralité du tableau.

function * range ( start, end, step = 1 ) {
  let state = start;
  while ( state < end ) {
    yield state;
    state += step;
  }
  return;
};

Vous pouvez maintenant créer quelque chose qui pré-génère le tableau à partir de l'itérateur et renvoie une liste. Ceci est utile pour les fonctions qui acceptent un tableau. Pour cela, nous pouvons utiliserArray.from()

const generate_array = (start,end,step) =>
  Array.from( range(start,end,step) );

Vous pouvez maintenant générer facilement un tableau statique,

const array1 = generate_array(1,10,2);
const array1 = generate_array(1,7);

Mais lorsque quelque chose désire un itérateur (ou vous donne la possibilité d'utiliser un itérateur), vous pouvez facilement en créer un aussi.

for ( const i of range(1, Number.MAX_SAFE_INTEGER, 7) ) {
  console.log(i)
}

Notes spéciales

Evan Carroll
la source
7

Bien que ce n'est pas de PHP , mais une imitation de rangede Python .

function range(start, end) {
    var total = [];

    if (!end) {
        end = start;
        start = 0;
    }

    for (var i = start; i < end; i += 1) {
        total.push(i);
    }

    return total;
}

console.log(range(10)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
console.log(range(0, 10)); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(range(5, 10)); // [5, 6, 7, 8, 9] 

la source
+1 pour le plus rapide. avec un tableau de -36768 - 36768, a pris 3 ms, la 2e place était de 13 ms et a des lignes rouges IDE.
mjwrazor
7

En ce qui concerne la génération d'un tableau numérique pour une plage donnée, j'utilise ceci:

function range(start, stop)
{
    var array = [];

    var length = stop - start; 

    for (var i = 0; i <= length; i++) { 
        array[i] = start;
        start++;
    }

    return array;
}

console.log(range(1, 7));  // [1,2,3,4,5,6,7]
console.log(range(5, 10)); // [5,6,7,8,9,10]
console.log(range(-2, 3)); // [-2,-1,0,1,2,3]

De toute évidence, cela ne fonctionnera pas pour les tableaux alphabétiques.

jhaskell
la source
La configuration array = []à l'intérieur de la boucle peut ne pas vous donner ce que vous voulez.
alex
@alex, merci. Tu as raison, j'ai aussi oublié d'incrémenter le paramètre "start" à chaque passage de la boucle. C'est réparé maintenant.
jhaskell
Il ne produira toujours pas la sortie souhaitée, si je veux une plage de 5 à 10, cela me donnera [5, 6, 7, 8, 9, 10, 11, 12, 13, 14], je ne m'attendrais qu'à la première moitié de ce tableau.
alex
@alex, merci encore, je n'avais pas envisagé de contrainte de longueur basée sur la saisie. Voir la version mise à jour.
jhaskell
6

Utilisation de générateurs Harmony , pris en charge par tous les navigateurs à l'exception d'IE11 :

var take = function (amount, generator) {
    var a = [];

    try {
        while (amount) {
            a.push(generator.next());
            amount -= 1;
        }
    } catch (e) {}

    return a;
};

var takeAll = function (gen) {
    var a = [],
        x;

    try {
        do {
            x = a.push(gen.next());
        } while (x);
    } catch (e) {}

    return a;
};

var range = (function (d) {
    var unlimited = (typeof d.to === "undefined");

    if (typeof d.from === "undefined") {
        d.from = 0;
    }

    if (typeof d.step === "undefined") {
        if (unlimited) {
            d.step = 1;
        }
    } else {
        if (typeof d.from !== "string") {
            if (d.from < d.to) {
                d.step = 1;
            } else {
                d.step = -1;
            }
        } else {
            if (d.from.charCodeAt(0) < d.to.charCodeAt(0)) {
                d.step = 1;
            } else {
                d.step = -1;
            }
        }
    }

    if (typeof d.from === "string") {
        for (let i = d.from.charCodeAt(0); (d.step > 0) ? (unlimited ? true : i <= d.to.charCodeAt(0)) : (i >= d.to.charCodeAt(0)); i += d.step) {
            yield String.fromCharCode(i);
        }
    } else {
        for (let i = d.from; (d.step > 0) ? (unlimited ? true : i <= d.to) : (i >= d.to); i += d.step) {
            yield i;
        }
    }
});

Exemples

prendre

Exemple 1.

take prend seulement autant que possible

take(10, range( {from: 100, step: 5, to: 120} ) )

Retour

[100, 105, 110, 115, 120]

Exemple 2.

to pas nécessaire

take(10, range( {from: 100, step: 5} ) )

Retour

[100, 105, 110, 115, 120, 125, 130, 135, 140, 145]

prendre toutes

Exemple 3.

from pas nécessaire

takeAll( range( {to: 5} ) )

Retour

[0, 1, 2, 3, 4, 5]

Exemple 4.

takeAll( range( {to: 500, step: 100} ) )

Retour

[0, 100, 200, 300, 400, 500]

Exemple 5.

takeAll( range( {from: 'z', to: 'a'} ) )

Retour

["z", "y", "x", "w", "v", "u", "t", "s", "r", "q", "p", "o", "n", "m", "l", "k", "j", "i", "h", "g", "f", "e", "d", "c", "b", "a"]

Janus Troelsen
la source
Modifié avec mes suggestions :)
Xotic750
+1 pour l'approche. Pour le point de @ alex, ne pas avoir d'opérations ternaires (en particulier non imbriquées) dans la forclause améliorerait la lisibilité ici.
Justin Johnson
5

... plus de portée grâce à une fonction générateur.

function range(s, e, str){
  // create generator that handles numbers & strings.
  function *gen(s, e, str){
    while(s <= e){
      yield (!str) ? s : str[s]
      s++
    }
  }
  if (typeof s === 'string' && !str)
    str = 'abcdefghijklmnopqrstuvwxyz'
  const from = (!str) ? s : str.indexOf(s)
  const to = (!str) ? e : str.indexOf(e)
  // use the generator and return.
  return [...gen(from, to, str)]
}

// usage ...
console.log(range('l', 'w'))
//=> [ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' ]

console.log(range(7, 12))
//=> [ 7, 8, 9, 10, 11, 12 ]

// first 'o' to first 't' of passed in string.
console.log(range('o', 't', "ssshhhooooouuut!!!!"))
// => [ 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 't' ]

// only lowercase args allowed here, but ...
console.log(range('m', 'v').map(v=>v.toUpperCase()))
//=> [ 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V' ]

// => and decreasing range ...
console.log(range('m', 'v').map(v=>v.toUpperCase()).reverse())

// => ... and with a step
console.log(range('m', 'v')
          .map(v=>v.toUpperCase())
          .reverse()
          .reduce((acc, c, i) => (i % 2) ? acc.concat(c) : acc, []))

// ... etc, etc.

J'espère que c'est utile.

John Swindin
la source
5

Mon collègue de golf de code est venu avec ceci (ES6), inclus:

(s,f)=>[...Array(f-s+1)].map((e,i)=>i+s)

non inclus:

(s,f)=>[...Array(f-s)].map((e,i)=>i+s)
Marc Sloth Eastman
la source
4

d3 a également une fonction de gamme intégrée. Voir https://github.com/mbostock/d3/wiki/Arrays#d3_range :

d3.range ([démarrer,] arrêter [, étape])

Génère un tableau contenant une progression arithmétique, similaire à la plage intégrée Python. Cette méthode est souvent utilisée pour itérer sur une séquence de valeurs numériques ou entières, telles que les index dans un tableau. Contrairement à la version Python, les arguments ne doivent pas nécessairement être des entiers, bien que les résultats soient plus prévisibles s'ils sont dus à la précision en virgule flottante. Si l'étape est omise, elle vaut par défaut 1.

Exemple:

d3.range(10)
// returns [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Bob Baxley
la source
Je n'ai jamais su que D3 existait. Ne va pas utiliser leur méthode range mais va utiliser ce package.
mjwrazor
Merci beaucoup. J'utilise D3 et recherchais une méthode JS native, ne sachant pas que je D3 la proposait déjà.
Cezar
4

Implémentation complète d'ES6 en utilisant la signature de la plage ([start,] stop [, step]):

function range(start, stop, step=1){
  if(!stop){stop=start;start=0;}
  return Array.from(new Array(int((stop-start)/step)), (x,i) => start+ i*step)
}

Si vous voulez un pas négatif automatique, ajoutez

if(stop<start)step=-Math.abs(step)

Ou plus minimaliste:

range=(b, e, step=1)=>{
  if(!e){e=b;b=0}
  return Array.from(new Array(int((e-b)/step)), (_,i) => b<e? b+i*step : b-i*step)
}

Si vous avez d'énormes gammes, regardez l'approche du générateur de Paolo Moretti

Anona112
la source
Remplacez !stoppar typeof stop === 'undefined', puis remplacez intpar Math.floor, et ajoutez une coche if (start > stop && step > 0)(sinon, range(-3, -10)lève une exception au lieu de faire quelque chose de sain (soit retourner le signe de l'étape, soit revenir [])). Sinon, bon!
Ahmed Fasih
4

Il y a un module npm bereich pour cela ("bereich" est le mot allemand pour "range"). Il utilise les itérateurs modernes de JavaScript, vous pouvez donc l'utiliser de différentes manières, telles que:

console.log(...bereich(1, 10));
// => 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

const numbers = Array.from(bereich(1, 10));
// => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

for (const number of bereich(1, 10)) {
  // ...
}

Il prend également en charge les plages décroissantes (en échangeant simplement minet max), et prend également en charge les étapes autres que 1.

Avertissement: je suis l'auteur de ce module, veuillez donc prendre ma réponse avec un grain de sel.

Golo Roden
la source
4

Celui-ci fonctionne également à l'envers.

const range = ( a , b ) => Array.from( new Array( b > a ? b - a : a - b ), ( x, i ) => b > a ? i + a : a - i );

range( -3, 2 ); // [ -3, -2, -1, 0, 1 ]
range( 1, -4 ); // [ 1, 0, -1, -2, -3 ]
Rocco Ghielmini
la source