Fonction JavaScript similaire à la gamme Python ()

96

Existe-t-il une fonction en JavaScript similaire à celle de Python range()?

Je pense qu'il devrait y avoir un meilleur moyen que d'écrire les lignes suivantes à chaque fois:

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}
Clwen
la source
1
@clwen: Malheureusement, il n'y en a pas, mais jetez un œil à mon code - j'ai écrit une fonction qui vise à émuler le range()fonctionnement de Python, vous pouvez donc l'utiliser. Il n'existe pas de fonction de ce type dans JavaScript, mais il existe des plugins pour différents frameworks, tels que la Rangeclasse pour MooTools .
Tadeck

Réponses:

89

Non , il n'y en a pas, mais vous pouvez en créer un .

L'implémentation JavaScript de Python range()

En essayant d' émuler son fonctionnement en Python , je créerais une fonction similaire à celle-ci:

function range(start, stop, step) {
    if (typeof stop == 'undefined') {
        // one param defined
        stop = start;
        start = 0;
    }

    if (typeof step == 'undefined') {
        step = 1;
    }

    if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
        return [];
    }

    var result = [];
    for (var i = start; step > 0 ? i < stop : i > stop; i += step) {
        result.push(i);
    }

    return result;
};

Voir ce jsfiddle pour une preuve.

Comparaison entre range()JavaScript et Python

Cela fonctionne de la manière suivante:

  • range(4)retourne [0, 1, 2, 3],
  • range(3,6)retourne [3, 4, 5],
  • range(0,10,2)retourne [0, 2, 4, 6, 8],
  • range(10,0,-1)retourne [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
  • range(8,2,-2)retourne [8, 6, 4],
  • range(8,2)retourne [],
  • range(8,2,2)retourne [],
  • range(1,5,-1)retourne [],
  • range(1,5,-2)retourne [],

et son homologue Python fonctionne exactement de la même manière (au moins dans les cas mentionnés):

>>> range(4)
[0, 1, 2, 3]
>>> range(3,6)
[3, 4, 5]
>>> range(0,10,2)
[0, 2, 4, 6, 8]
>>> range(10,0,-1)
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> range(8,2,-2)
[8, 6, 4]
>>> range(8,2)
[]
>>> range(8,2,2)
[]
>>> range(1,5,-1)
[]
>>> range(1,5,-2)
[]

Donc, si vous avez besoin d'une fonction pour fonctionner de la même manière que Python range(), vous pouvez utiliser la solution mentionnée ci-dessus.

Tadeck
la source
4
peut-être quelques vérifications défensives supplémentaires - assurez-vous que les arguments passés sont tous contraignants à des nombres et assurez-vous que stopc'est supérieur à start(et échangez-les sinon).
Russ Cam
1
@RussCam: Merci de l'avoir signalé. Je n'ai pas ajouté de vérifications défensives pour les types, etc., mais j'ai implémenté l'ordre inverse des éléments - cela fonctionne maintenant exactement de la même manière que l'homologue Python, lorsque le dernier paramètre est un entier négatif.
Tadeck
@RussCam: start >= stopconduire à un tableau vide est nécessaire si l'objectif est vraiment d'émuler la plage de Python. Et je dirais que c'est de toute façon plus intuitif.
1
@delnan: Vérifier peut être plus complexe, car simple start >= stopne suffit pas pour que cette fonction se comporte comme range()en Python. J'ai mis à jour ma réponse.
Tadeck
@delnan - Je ne connais pas l'implémentation Python. Je suppose que si cela ne sera utilisé que par des personnes familiarisées avec l'implémentation Python, il est logique de l'émuler :)
Russ Cam
125

Pour une gamme très simple en ES6:

let range = n => Array.from(Array(n).keys())

D' après le commentaire de bigOmega , cela peut être raccourci en utilisant la syntaxe Spread :

let range = n => [...Array(n).keys()]
Will Ediger
la source
41
Une version plus simple de ceci estlet range = n => [...Array(n).keys()]
bigOmega
2
@BharathRaja super, merci! Pourquoi pas const? J'essaierai d'utiliser const partout où je peux :) comme l'équivalent final de Java;)
Kjellski
Vous avez raison, j'essaie d'utiliser constautant que possible, mais ici ce sera juste une référence au tableau, donc le tableau serait toujours modifiable: 'D
bigOmega
1
@bigOmega Le consts'appliquerait à la fonction elle-même, pas à sa valeur de retour. Vous ne souhaitez probablement pas modifier la fonction de quelque manière que ce soit.
Solomon Ucko
7
Cette réponse ne tient pas compte d'un index de départ et de la possibilité d'augmenter la taille des pas.
Fluous
29

2018: cette réponse continue de recevoir des votes positifs, voici donc une mise à jour. Le code ci-dessous est obsolète, mais heureusement, les générateurs standardisés ES6 et le yieldmot - clé, et ils sont universellement pris en charge sur toutes les plates-formes. Un exemple d' range()utilisation paresseuse yieldpeut être trouvé ici .


En plus de ce qui a déjà été dit, Javascript 1.7+ fournit un support pour les itérateurs et les générateurs qui peuvent être utilisés pour créer une version paresseuse et économe en mémoire de range, similaire à xrangedans Python2:

function range(low, high) {  
    return {
        __iterator__: function() {
            return {  
                next: function() {
                    if (low > high)
                        throw StopIteration;  
                    return low++;
                }
            }
        }
    }
}

for (var i in range(3, 5))  
  console.log(i); // 3,4,5
Georg
la source
1
+1 Excellente idée! Pourriez-vous également implémenter l' stepargument et le tester sur les valeurs de ma réponse ? Votre réponse est excellente pour les applications pour lesquelles nous avons à l'esprit des navigateurs très spécifiques (cela ne fonctionnera pas dans les versions Google Chrome, Safari et IE antérieures à la 9: stackoverflow.com/a/2209743/548696 ).
Tadeck
@Tadeck: ironiquement, j'ai posé une question très similaire récemment, vérifiez-la - quelques bonnes réponses là-bas. BTW, votre code ne passe pas mon test; (
georg
Pourriez-vous partager les données de test et les résultats attendus? Je serais heureux de l'améliorer, mais mes tests réussissent à 100%. Êtes-vous en train de dire que le code que j'ai donné n'est pas correctement analysé par le script que vous avez placé dans cette question: stackoverflow.com/q/12173856/548696 ?
Tadeck
@Tadeck: tant pis. J'ai testé des tranches et votre code est pour les plages.
georg
1
Python rangea la "dernière" valeur exclue (il faut donc l'utiliser à la >=place du >code ci-dessus)
Pac0
23

En fusionnant les deux réponses de @Tadeck et @georg , j'ai trouvé ceci:

function* range(start, stop, step = 1) {
    if (stop == null) {
        // one param defined
        stop = start;
        start = 0;
    }

    for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
        yield i;
    }
}

Pour l'utiliser dans une boucle for, vous avez besoin de la boucle for-of ES6 / JS1.7:

for (let i of range(5)) {
    console.log(i);
}
// Outputs => 0 1 2 3 4

for (let i of range(0, 10, 2)) {
    console.log(i);
}
// Outputs => 0 2 4 6 8

for (let i of range(10, 0, -2)) {
    console.log(i);
}
// Outputs => 10 8 6 4 2
janka102
la source
Pourquoi ne pas utiliser les paramètres par défaut si vous utilisez ES6?
Amin NAIRI
@Gradiuss qui fonctionnerait pour le stepparamètre, mais quand stopn'est pas passé, les deux startet stopdoivent être modifiés. Je vais le mettre à jour
janka102
2
WTG, c'est la meilleure implémentation présentée à ce jour. Il utilise des générateurs (bien, car il n'est pas nécessaire de stocker toute la séquence en mémoire) et est très succinct.
Lucio Paiva
undefined est un pointeur vers un objet tel que null. possible de comparer avec 3 signes égaux comme: if (stop === undefined) {3 signes égaux est comparer sans casting automatique. comparer comme comparer le type. 2 signes égaux sont à comparer avec le casting automatique vers un autre type de côté.
Shimon Doodkin
20

Un portage de la rangefonction à partir de Python 2 est fourni par les bibliothèques utilitaires underscore.js et lodash (avec de nombreux autres outils utiles). Exemples copiés à partir des documents de soulignement:

_.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);
=> []
Mark Amery
la source
15

Peut être réalisé en attachant un itérateur au Numberprototype

  Number.prototype[Symbol.iterator] = function* () { 
     for (var i = 0; i <= this; i++) {
       yield i
     } 
  }

[...5] // will result in [0,1,2,3,4,5]

Tiré du cours de Kyle Simpson Rethinking Asynchronous JavaScript

mcha
la source
7
C'est vraiment cool, mais remplacer les prototypes est tout simplement trop fragile: '(
m0meni
8

Voici une petite extension pour l'une des réponses au cas où vous auriez besoin de spécifier à la fois la position de début et de fin de la plage:

let range = (start, end) => Array.from(Array(end + 1).keys()).slice(start);
Dmitrii Mikhailov
la source
7

Voici.

Cela écrira (ou écrasera) la valeur de chaque index avec le numéro d'index.

Array.prototype.writeIndices = function( n ) {
    for( var i = 0; i < (n || this.length); ++i ) this[i] = i;
    return this;
};

Si vous ne fournissez pas de nombre, il utilisera la longueur actuelle du tableau.

Utilisez-le comme ceci:

var array = [].writeIndices(10);  // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
RightSaidFred
la source
5

Pour obtenir un tableau de taille x, voici un one-liner sans utiliser aucune bibliothèque

var range = n => Array(n + 1).join(1).split('').map((x, i) => i)

travailler en tant que

> range(4)
[0, 1, 2, 3]
bigOmega
la source
2
var range = n => Array(n).fill().map((e, i) => i);
Valen
et pourquoi dire «taille x» quand vous l'avez réellement utilisé ncomme nom de paramètre
Valen
5

Ce qui suit est une adaptation naturelle de la fonction range () de Python à JavaScript:

// Generate range from start (inclusive) to stop (exclusive):
function* range(start, stop, step = 1) {
   if (stop === undefined) [start, stop] = [0, start];
   if (step > 0) while (start < stop) yield start, start += step;
   else if (step < 0) while (start > stop) yield start, start += step;
   else throw new RangeError('range() step argument invalid');
} 

// Examples:
console.log([...range(3)]);       // [0, 1, 2]
console.log([...range(0, 3)]);    // [0, 1, 2]
console.log([...range(0, 3, -1)]);// []
console.log([...range(0, 0)]);    // []
console.log([...range(-3)]);      // []
console.log([...range(-3, 0)]);   // [-3, -2, -1]

Il prend en charge tout argument qui peut être comparé à 0et stopet qui peut être incrémenté de step. Il se comporte de la même manière que la version Python lorsqu'il est utilisé avec des nombres ne dépassant pas Number.MAX_SAFE_INTEGER.

Veuillez noter les cas d'angle suivants:

[...range(0, 0, 0)];        // RangeError: range() step argument invalid
[...range(Number.MAX_SAFE_INTEGER + 1, Number.MAX_SAFE_INTEGER + 2)];  // []
[...range(Number.MAX_SAFE_INTEGER + 2, Number.MAX_SAFE_INTEGER + 3)];  // Infinite loop
[...range(0.7, 0.8, 0.1)];  // [0.7, 0.7999999999999999]
[...range('1', '11')];      // ['1']
[...range('2', '22')];      // Infinite loop

Contrairement à @ Tadeck de , @ de Volv et de @ janka102 réponse qui retour [], undefinedou entrer dans une boucle infinie lorsque stepevalue à 0ou NaN, cette fonction de générateur renvoie une exception similaire au comportement de Python.

le_m
la source
D'accord. Bien que les autres réponses soient élégantes à leur manière, cette approche et cette fonctionnalité sont beaucoup plus pythoniques.
Travis Clarke
4

Plus raffiné avec les paramètres par défaut de ES6.

let range = function*(start = 0, stop, step = 1) {
  let cur = (stop === undefined) ? 0 : start;
  let max = (stop === undefined) ? start : stop;
  for (let i = cur; step < 0 ? i > max : i < max; i += step)
    yield i
}
Volv
la source
Voici à quoi devrait ressembler la gamme.
Konrad Linkowski
4

pythonicimite au rangemieux le comportement Python en utilisant les générateurs de JS ( yield), prenant en charge à la fois les cas d'utilisation range(stop)et range(start, stop, step). De plus, pythonicla rangefonction de retourne un Iteratorobjet similaire à Python qui prend en charge mapet filter, donc on pourrait faire des one-liners sophistiqués comme:

import {range} from 'pythonic';
// ...
const results = range(5).map(wouldBeInvokedFiveTimes);
// `results` is now an array containing elements from
// 5 calls to wouldBeInvokedFiveTimes

Installez en utilisant npm:

npm install --save pythonic

Divulgation Je suis l'auteur et le mainteneur de Pythonic

Keyvan
la source
3

MDN recommande cette approche: Générateur de séquence (plage)

// Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));

// Generate numbers range 0..4
console.log("range(0, 4, 1):", range(0, 4, 1));
// [0, 1, 2, 3, 4] 

// Generate numbers range 1..10 with step of 2 
console.log("\nrange(1, 10, 2):", range(1, 10, 2));
// [1, 3, 5, 7, 9]

// Generate the alphabet using Array.from making use of it being ordered as a sequence
console.log("\nrange('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x))", range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x)));
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

IliasT
la source
2

Vous pouvez utiliser la bibliothèque de soulignement . Il contient des dizaines de fonctions utiles pour travailler avec des tableaux et bien d'autres.

Radagast
la source
2

Existe-t-il une fonction en JavaScript similaire à range () de Python?

Toutes les solutions ici font référence à la gamme de Python 2 (probablement à cause de l'exemple de code que vous avez donné). Cependant, dans Python 3, la méthode range () retourne un itérateur. JavaScript a également des itérateurs et ils sont plus efficaces en termes d'espace que de générer l'ensemble du tableau et de le stocker en mémoire.

La représentation la plus précise de la range(n)fonction de Python 3 est donc Array(n).keys().

Par exemple:

for (let i of Array(n).keys()) {
  console.log(i) // 0, 1, 2, 3, ..., n
}

Un autre exemple (qui a déjà été traité dans les autres réponses). Conversion de l'itérateur en tableau (ES6):

let ary = [...Array(n).keys()];
// ary = [0, 1, 2, 3, ..., n]
MattCochrane
la source
1

Toujours pas de fonction intégrée équivalente à range(), mais avec la version la plus récente - ES2015 - vous pouvez créer votre propre implémentation. En voici une version limitée. Limité car il ne prend pas en compte le paramètre step. Juste min, max.

const range = (min = null, max = null) =>
  Array.from({length:max ? max - min : min}, (v,k) => max ? k + min : k)

Ceci est accompli par la Array.fromméthode capable de créer un tableau à partir de n'importe quel objet possédant une lengthpropriété. Donc, passer un objet simple avec juste la lengthpropriété créera un ArrayIterator qui produira le lengthnombre d'objets.

Steve Brownlee
la source
1

C'est ma manière préférée. Il vous permet de spécifier une ou deux entrées comme en Python.

function range(start, end) {
  return Array.from(Array(end||start).keys()).slice(!!end*start)
}
Rob Kwasowski
la source
0

Voici une autre es6implémentation de la gamme

// range :: (from, to, step?) -> [Number]
const range = (from, to, step = 1) => {
  //swap values if necesery
  [from, to] = from > to ? [to, from] : [from, to]
  //create range array
  return [...Array(Math.round((to - from) / step))]
    .map((_, index) => {
      const negative = from < 0 ? Math.abs(from) : 0
      return index < negative ? 
        from + index * step  :
        (index - negative + 1) * step
    })
}  

range(-20, 0, 5)
  .forEach(val => console.log(val))

for(const val of range(5, 1)){
   console.log(`value ${val}`)
}

mrFunkySagesse
la source
Et si nous voulons générer un tableau de -20à -30?
Amin NAIRI
Hmm, celui-ci générera un tableau de -30 à -20, mais il peut être facilement modifié pour inclure une propriété inverse si vous le souhaitez. Je modifierai la réponse ci-dessus pour l'inclure
mrFunkyWisdom
0

Non, il n'y en a pas, mais vous pouvez en créer un.

Je suis partial pour le comportement de la plage Python3. Vous trouverez ci-dessous l'implémentation JavaScript de la gamme Python ():

function* range(start=0, end=undefined, step=1) {    
    if(arguments.length === 1) {end = start, start = 0}    
    
    [...arguments].forEach(arg => {    
        if( typeof arg !== 'number') {throw new TypeError("Invalid argument")}                               
    })    
    if(arguments.length === 0) {throw new TypeError("More arguments neede")}    
        
    if(start >= end) return                                                                                                                                     
    yield start    
    yield* range(start + step, end, step)    
}    
         
// Use Cases
console.log([...range(5)])

console.log([...range(2, 5)])

console.log([...range(2, 5, 2)])
console.log([...range(2,3)])
// You can, of course, iterate through the range instance.

elayira
la source
0

En supposant que vous ayez besoin d'une gamme simple en une seule étape:

let range = (start, end)=> {
    if(start === end) return [start];
    return [start, ...range(start + 1, end)];
}

autre

let range = (start, end, step)=> {
    if(start === end) return [start];
    return [start, ...range(start + step, end)];
}

reportez-vous ici pour en savoir plus.

N Djel Okoye
la source
0

Existe-t-il une fonction en JavaScript similaire à range () de Python?

Comme répondu précédemment: non , il n'y en a pas. Mais vous pouvez créer le vôtre. Je pense que c'est une approche intéressante pour ES6. Cela fonctionne très similaire à Python 2.7 range(), mais c'est beaucoup plus dynamique.

function range(start, stop, step = 1) 
{
    // This will make the function behave as range(stop)
    if(arguments.length === 1)
    {
        return [...Array(arguments[0]).keys()]
    }

    // Adjusts step to go towards the stop value
    if((start > stop && !(step < 0)) ||
       (start < stop && !(step > 0)))
    {
        step *= -1
    }

    let returnArray = []
    // Checks if i is in the interval between start and stop no matter if stop
    // is lower than start or vice-versa
    for(let i = start; (i-start)*(i-stop) <= 0; i += step)
    {
        returnArray.push(i)
    }
    return returnArray
}

Cette fonction peut se comporter de trois manières différentes (tout comme le range () de Python):

  1. range(stop)
  2. range(start, stop)
  3. range(start, stop, step)

Ces exemples:

console.log(range(5))
console.log(range(-2, 2))
console.log(range(2, -2))
console.log(range(10, 20, 2))

Vous donnera la sortie suivante:

[ 0, 1, 2, 3, 4 ]
[ -2, -1, 0, 1, 2 ]
[ 2, 1, 0, -1, -2 ]
[ 10, 12, 14, 16, 18, 20 ]

Notez qu'au lieu d'itérer sur le tableau avec l' inopérateur (comme python), vous devez utiliser of. Ainsi, la ivariable prend la valeur, et non l'index, de l'élément du tableau.

for(let i of range(5))
{
    // do something with i...
}
sndmnn
la source
0

Une option pour NodeJs est d'utiliser un tampon:

[...Buffer.alloc(5).keys()]
// [ 0, 1, 2, 3, 4 ]

Ce qui est bien, c'est que vous pouvez itérer directement sur le tampon:

Buffer.alloc(5).forEach((_, index) => console.log(index))
// 0
// 1
// 2
// 3
// 4

Vous ne pouvez pas faire cela avec un tableau non initialisé:

Array(5).forEach((_, index) => console.log(index))
// undefined

Mais, qui dans leur bon esprit utilise un tampon dans un but comme celui-ci;)

Otto
la source
-1

Voici comment je fais

let n = 5 
[...Array(n).keys()].map(x=>{console.log(x)})

production

0
1
2
3
4
Ricky
la source
Cela ne prend pas en charge les étapes.
Mark Stosberg