Función de JavaScript similar al rango de Python ()

96

¿Existe una función en JavaScript similar a la de Python range()?

Creo que debería haber una mejor manera que escribir las siguientes líneas cada vez:

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}
clwen
fuente
1
@clwen: Desafortunadamente no lo hay, pero eche un vistazo a mi código: escribí una función que tiene como objetivo emular la forma range()en que funciona en Python, para que pueda usarla. No existe tal función en JavaScript, pero hay algunos complementos para diferentes marcos, como la Rangeclase para MooTools .
Tadeck

Respuestas:

89

No , no hay ninguno, pero puedes hacer uno .

Implementación de JavaScript de Python range()

Tratando de emular cómo funciona en Python , crearía una función similar a esta:

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

Vea este jsfiddle para una prueba.

Comparación entre range()JavaScript y Python

Funciona de la siguiente manera:

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

y su contraparte de Python funciona exactamente de la misma manera (al menos en los casos mencionados):

>>> 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)
[]

Entonces, si necesita una función que funcione de manera similar a la de Python range(), puede usar la solución mencionada anteriormente.

Tadeck
fuente
4
tal vez un par de comprobaciones defensivas adicionales: asegúrese de que los argumentos pasados ​​sean todos coercibles a números y asegúrese de que stopsea ​​mayor que start(y cámbielos si no).
Russ Cam
1
@RussCam: Gracias por señalar esto. No agregué controles defensivos para tipos, etc., pero implementé el orden inverso de los elementos; ahora funciona exactamente igual que la contraparte de Python, cuando el último parámetro es un entero negativo.
Tadeck
@RussCam: start >= stopes necesario llevar a una matriz vacía si el objetivo es realmente emular el rango de Python. Y yo diría que es más intuitivo de todos modos.
1
@delnan: La verificación puede ser más compleja, ya que simple start >= stopno es suficiente para que esta función se comporte como range()en Python. He actualizado mi respuesta.
Tadeck
@delnan: no estoy familiarizado con la implementación de Python. Supongo que si solo lo usarán personas familiarizadas con la implementación de Python, tiene sentido emularlo :)
Russ Cam
125

Para un rango muy simple en ES6:

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

Del comentario de bigOmega , esto se puede acortar usando la sintaxis de Spread :

let range = n => [...Array(n).keys()]
Will Ediger
fuente
41
Una versión más simple de esto eslet range = n => [...Array(n).keys()]
bigOmega
2
@BharathRaja genial, ¡gracias! ¿Por qué no const? Intentaré usar const siempre que pueda :) como la contraparte final de Java;)
Kjellski
Tiene razón, trato de usar consttanto como sea posible, pero aquí solo será una referencia a la matriz, por lo que la matriz aún sería editable: 'D
bigOmega
1
@bigOmega El constse aplicaría a la función en sí, no a su valor de retorno. Probablemente no desee modificar la función de ninguna manera.
Solomon Ucko
7
Esta respuesta no tiene en cuenta un índice inicial y la capacidad de aumentar el tamaño del paso.
Fluous
29

2018: esta respuesta sigue recibiendo votos a favor, así que aquí hay una actualización. El código a continuación es obsoleto, pero afortunadamente los generadores estandarizados ES6 y la yieldpalabra clave, y son universalmente compatibles en todas las plataformas. Aquí se puede encontrar un ejemplo del range()uso perezoso . yield


Además de lo que ya se ha dicho, Javascript 1.7+ proporciona soporte para iteradores y generadores que se pueden usar para crear una versión perezosa y eficiente en memoria de range, similar a xrangeen 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
fuente
1
+1 ¡Gran idea! ¿Podría implementar también el stepargumento y probarlo con los valores de mi respuesta ? Su respuesta es excelente para las aplicaciones en las que tenemos navegadores muy específicos en mente (no funcionará en Google Chrome, Safari e IE versiones anteriores a la 9: stackoverflow.com/a/2209743/548696 ).
Tadeck
@Tadeck: irónicamente, hice una pregunta muy similar recientemente, compruébalo, hay algunas buenas respuestas allí. Por cierto, su código no pasa mi prueba; (
georg
¿Podría compartir los datos de la prueba y los resultados esperados? Me encantaría mejorarlo, pero mis pruebas están pasando al 100%. ¿Está diciendo que el código que he proporcionado no está analizado correctamente por el script que ha colocado en esta pregunta: stackoverflow.com/q/12173856/548696 ?
Tadeck
@Tadeck: no importa. Probé porciones y su código es para rangos.
georg
1
Python rangetiene el "último" valor excluido (por lo que se debe usar en >=lugar del >código anterior)
Pac0
23

Fusionando ambas respuestas de @Tadeck y @georg , se me ocurrió esto:

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

Para usarlo en un bucle for, necesita el bucle for-of de 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
fuente
¿Por qué no utilizar los parámetros predeterminados si está utilizando ES6?
Amin NAIRI
@Gradiuss que trabajaría para el stepparámetro, pero cuando stopno se pasa en tanto starty stoptienen que ser modificado. Lo actualizaré
janka102
2
WTG, esta es la mejor implementación presentada hasta ahora. Utiliza generadores (bueno, ya que no es necesario almacenar toda la secuencia en la memoria) y es muy conciso.
Lucio Paiva
undefined es un puntero a un objeto como null. posible comparar con 3 signos iguales como: if (stop === undefined) {3 signos iguales se compara sin conversión automática. compare como es también compare el tipo. 2 signos iguales se comparan con la fundición automática a otro tipo de lado.
Shimon Doodkin
20

Las bibliotecas de utilidades underscore.js y lodashrange proporcionan un puerto de la función de Python 2 (junto con muchas otras herramientas útiles). Ejemplos copiados de los documentos de subrayado:

_.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
fuente
15

Se puede lograr adjuntando un iterador al Numberprototipo

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

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

Tomado del curso de Kyle Simpson Rethinking Asynchronous JavaScript

mcha
fuente
7
Esto es realmente genial, pero anular los prototipos simplemente es demasiado frágil: '(
m0meni
8

Aquí hay una pequeña extensión para una de las respuestas en caso de que necesite especificar la posición inicial y final del rango:

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

Aqui tienes.

Esto escribirá (o sobrescribirá) el valor de cada índice con el número de índice.

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

Si no proporciona un número, utilizará la longitud actual de la matriz.

Úselo así:

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

Para obtener una variedad de tamaños x, aquí hay una sola línea sin usar ninguna biblioteca

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

trabaja como

> range(4)
[0, 1, 2, 3]
bigOmega
fuente
2
var range = n => Array(n).fill().map((e, i) => i);
Valen
y por qué decir 'tamaño x' cuando en realidad lo usó ncomo nombre de parámetro
Valen
5

La siguiente es una adaptación natural de la función range () de Python a 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]

Es compatible con cualquier argumento que puede ser comparado a 0y stopy puede ser incrementado por step. Se comporta de manera idéntica a la versión de Python cuando se usa con números que no exceden Number.MAX_SAFE_INTEGER.

Tenga en cuenta los siguientes casos de esquina:

[...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

En contraste con @ Tadeck de , @ de Volv y @ de janka102 respuesta que el retorno [], undefinedo entrar en un bucle infinito cuando stepse evalúa como 0o NaN, esta función generador genera una excepción similar al comportamiento del pitón.

le_m
fuente
Convenido. Aunque las otras respuestas son elegantes a su manera, este enfoque y funcionalidad es mucho más pitónico.
Travis Clarke
4

Más refinado con los parámetros predeterminados 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
fuente
Así es como debería verse realmente el rango.
Konrad Linkowski
4

pythonicimita el pitón rangecomportamiento mejor que puede usar generadores JS'( yield), apoyando tanto las range(stop)y range(start, stop, step)los casos de uso. Además, pythonicla rangefunción 'devuelve un Iteratorobjeto similar a Python que admite mapy filter, por lo que uno podría hacer frases ingeniosas como:

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

Instalar usando npm:

npm install --save pythonic

Divulgación Soy autor y mantenedor de Pythonic

Keyvan
fuente
3

MDN recomienda este enfoque: generador de secuencia (rango)

// 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
fuente
2

Puede utilizar la biblioteca de subrayado . Contiene docenas de funciones útiles para trabajar con matrices y muchas más.

Radagast
fuente
2

¿Existe una función en JavaScript similar al rango de Python ()?

Todas las soluciones aquí se refieren al rango de Python 2 (probablemente debido al ejemplo de código que dio). Sin embargo, en Python 3, el método range () devuelve un iterador. JavaScript también tiene iteradores y son más eficientes en el espacio que generar toda la matriz y almacenarla en la memoria.

Entonces, la representación más precisa de la range(n)función de Python 3 es Array(n).keys().

Por ejemplo:

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

Un ejemplo más (que ya se ha cubierto en las otras respuestas). Conversión del iterador en una matriz (ES6):

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

Aún no hay una función incorporada que sea equivalente range(), pero con la versión más reciente, ES2015, puede crear su propia implementación. Aquí tienes una versión limitada. Limitado porque no tiene en cuenta el parámetro de paso. Solo min, max.

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

Esto se logra mediante el Array.frommétodo capaz de construir una matriz a partir de cualquier objeto que tenga una lengthpropiedad. Entonces, pasar un objeto simple con solo la lengthpropiedad creará un ArrayIterator que producirá una lengthcantidad de objetos.

Steve Brownlee
fuente
1

Esta es mi forma preferida. Te permite especificar una o dos entradas como en Python.

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

Aquí hay otra es6implementación de la gama.

// 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}`)
}

mrFunkyWisdom
fuente
¿Qué pasa si queremos generar una matriz desde -20hasta -30?
Amin NAIRI
Hmm, este generará una matriz de -30 a -20, pero se puede modificar fácilmente para incluir una propiedad inversa si lo desea.
Editaré
0

No, no hay ninguno, pero puedes hacer uno.

Soy parcial al comportamiento de rango de Python3. A continuación, encontrará la implementación de JavaScript del rango de 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
fuente
0

Suponiendo que necesita un rango simple con un solo paso:

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

más

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

consulte aquí para obtener más información.

N Djel Okoye
fuente
0

¿Existe una función en JavaScript similar al rango de Python ()?

Como respondí antes: no , no lo hay. Pero puedes hacer el tuyo propio. Creo que este es un enfoque interesante para ES6. Funciona de manera muy similar a Python 2.7 range(), pero es mucho más dinámico.

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
}

Esta función puede comportarse de tres formas diferentes (como el range () de Python):

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

Estos ejemplos:

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

Le dará el siguiente resultado:

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

Tenga en cuenta que en lugar de iterar sobre la matriz con el inoperador (como python), debe usar of. Por tanto, la ivariable asume el valor, y no el índice, del elemento de la matriz.

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

Una opción para NodeJs es usar un búfer:

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

Lo bueno es que puede iterar directamente en el búfer:

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

No puede hacer eso con una matriz no inicializada:

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

Pero, ¿quién en su sano juicio usa un búfer para un propósito como este;)

Otón
fuente
-1

Así es como lo hago

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

salida

0
1
2
3
4
Ricky
fuente
Esto no admite pasos.
Mark Stosberg