Функция JavaScript похожа на диапазон Python ()

Есть ли в JavaScript функция, аналогичная Python range()?

Я думаю, что должен быть лучший способ, чем каждый раз писать следующие строки:

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}

32 ответа

Решение

Нет, нет, но вы можете сделать один.

JavaScript реализация Python range()

Пытаясь эмулировать, как это работает в Python, я бы создал функцию, подобную этой:

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

Смотрите это jsfiddle для доказательства.

Сравнение между range() в JavaScript и Python

Это работает следующим образом:

  • 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) возвращается [],

и его аналог Python работает точно так же (по крайней мере, в упомянутых случаях):

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

Так что если вам нужна функция, чтобы работать аналогично Python range()Вы можете использовать вышеупомянутое решение.

Для очень простого диапазона в ES6:

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

Соединяя оба ответа от @Tadeck и @georg, я придумал следующее:

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

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

Чтобы использовать его в цикле for, вам необходим цикл for-of ES6/JS1.7:

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

2018: этот ответ продолжает получать голоса, так что вот обновление. Приведенный ниже код устарел, но, к счастью, стандартные генераторы ES6 и yield ключевое слово, и они универсально поддерживаются на разных платформах. Пример ленивый range() с помощью yield можно найти здесь.


В дополнение к тому, что уже было сказано, Javascript 1.7+ обеспечивает поддержку итераторов и генераторов, которые можно использовать для создания отложенной версии с эффективным использованием памяти. range, одновременно xrange в 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

Порт Питона range Функция предоставляется утилитами http://underscorejs.org/ и lodash (наряду со многими другими полезными инструментами). Примеры, скопированные из документов с подчеркиванием:

_.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);
=> []

Может быть достигнуто путем присоединения итератора к Number прототип

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

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

Взято из курса Кайла Симпсона Переосмысление асинхронного JavaScript

Вот небольшое расширение для одного из ответов на тот случай, если вам нужно указать начальную и конечную позиции диапазона:

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

pythonic подражает питону range поведение лучше, чем это возможно, используя генераторы JS (yield), поддерживая оба range(stop) а также range(start, stop, step) случаи применения. К тому же, pythonic "s range функция возвращает пользовательский Generator объект, который поддерживает map а также filter Таким образом, можно сделать такие однорядные строки:

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

Установить с помощью npm:

npm install --save pythonic

Вот код в pythonic для диапазона:

function range(...args) {
    if (args.length < 2) {
        return new Generator(rangeGeneratorWithStop(...args));
    }
    return new Generator(rangeGeneratorWithSartAndStopAndStep(...args));
}

const rangeGeneratorWithStop = stop => function * () {
    for (let i = 0; i < stop; i++) {
        yield i;
    }
};

const rangeGeneratorWithSartAndStopAndStep = (start, stop, step = 1) => function * () {
    for (let i = start; i < stop; i += step) {
        yield i;
    }
};

function range(...args) {
    if (args.length < 2) {
        return new Generator(rangeGeneratorWithStop(...args));
    }
    return new Generator(rangeGeneratorWithSartAndStopAndStep(...args));
}

class Generator {
    constructor(generatorFn) {
        this[Symbol.iterator] = generatorFn;
    }

    map(callbackFn) {
        const result = [];
        for (const element of this) {
            result.push(callbackFn(element));
        }
        return result;
    }

    filter(callbackFn) {
        const result = [];
        for (const element of this) {
            if (callbackFn(element)) {
                result.push(element);
            }
        }
        return result;
    }

    toArray() {
        return Array.from(this);
    }
}

Ну вот.

Это запишет (или перезапишет) значение каждого индекса с номером индекса.

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

Если вы не предоставите номер, он будет использовать текущую длину массива.

Используйте это так:

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

Ниже приведена естественная адаптация функции Python range() к 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]

Он поддерживает любой аргумент, который можно сравнить с 0 а также stop и может быть увеличен на step, Он ведет себя идентично версии Python при использовании с числами, не превышающими Number.MAX_SAFE_INTEGER,

Обратите внимание на следующие угловые случаи:

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

В отличие от ответов @Tadeck, @ Volv и @janka102, которые возвращаются [], undefined или введите бесконечный цикл, когда step оценивает 0 или же NaN эта функция генератора генерирует исключение, похожее на поведение Python.

Далее уточняется с параметрами по умолчанию 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
}

Для тех, кто ищет современное решение[...Array(n).keys()]

Для получения массива размера x, вот одна строка без использования какой-либо библиотеки

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

работает как

> range(4)
[0, 1, 2, 3]

MDN рекомендует такой подход: генератор последовательностей (диапазон)

Фактически, в Python range () возвращает итерируемый объект, и мы знаем, что итераторы более эффективны с точки зрения памяти, чем массивы (или списки в Python). Поэтому, если мы хотим реализовать ту же концепцию с точной функциональностью в JavaScript, мы можем использовать объект итератора:

      class range {

constructor(start, stop, step = 1) {
    //check for invalid input
    if (stop !== undefined && typeof stop !== 'number'
        || typeof start !== 'number'
        || typeof step !== 'number') {
        throw Error('invalid input for range function');
    }

    //check if second argument is provided
    if (stop === undefined) {
        stop = start;
        start = 0;
    }

    //initialize the object properties
    this.start = start;
    this.stop = stop;
    this.step = step;
}

//create the iterator object with Symbol.iterator
[Symbol.iterator]() {
    return {
        current: this.start,
        last: this.stop,
        step: this.step,
        //implement the next() method of the iterator
        next() {
            if (this.step === 0) {
                return { done: true };
            } else if (this.step > 0 ? this.current < this.last : this.current > this.last) {
                let value = this.current;
                this.current += this.step;
                return { done: false, value };
            } else {
                return { done: true };
            }
        }
    };
};
}

и например у нас есть:

      for (const num of new range(1, 10, 2)) {
console.log(num);
}

также мы можем легко создать массив:

      let arr = [...new range(10, -5, -1)];

или же:

      let arr = Array.from(new range(10));

Есть ли в JavaScript функция, похожая на Python range()?

Все решения здесь относятся к диапазону Python 2 (вероятно, из-за приведенного вами примера кода). Однако в Python 3 метод range () возвращает итератор. В JavaScript также есть итераторы, и они занимают больше места, чем создание всего массива и сохранение его в памяти.

Итак, более точное представление Python 3 range(n) функция Array(n).keys().

Например:

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

Еще один пример (который уже был рассмотрен в других ответах). Преобразование итератора в массив (ES6):

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

Вы можете использовать библиотеку подчеркивания. Он содержит десятки полезных функций для работы с массивами и многое другое.

Все еще нет встроенной функции, которая эквивалентна range(), но с самой последней версией - ES2015 - вы можете создать свою собственную реализацию. Вот ограниченная версия этого. Ограничено, поскольку не учитывает параметр шага. Просто мин, макс.

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

Это достигается Array.from метод, способный построить массив из любого объекта, который имеет length имущество. Таким образом, передавая в простой объект только с length свойство создаст ArrayIterator, который выдаст length количество предметов.

Это мой предпочтительный способ. Он позволяет вам указать один или два входа, как в Python.

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

Есть ли в JavaScript функция, похожая на Python range()?

Как ответили ранее: нет, нет. Но вы можете сделать свой собственный. Я считаю, что это интересный подход для ES6. Он работает очень похоже на Python 2.7.range(), но он намного динамичнее.

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
}

Эта функция может вести себя тремя разными способами (точно так же, как Python range()):

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

Эти примеры:

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

Вы получите следующий результат:

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

Обратите внимание, что вместо перебора массива с in оператор (например, python), вы должны использовать of. Таким образомi переменная принимает значение, а не индекс элемента массива.

for(let i of range(5))
{
    // do something with i...
}
function range(start, stop) {
    if (typeof stop == 'undefined') {
        stop = start;
        start = 0;
    }
   
    result = [...Array(stop).keys()].slice(start, stop);
    return result;
}

Вариант для NodeJs - использовать буфер:

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

Что приятно, так это то, что вы можете выполнять итерацию прямо в буфере:

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

Вы не можете сделать это с неинициализированным массивом:

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

Но кто в здравом уме использует буфер для такой цели;)

Предполагая, что вам нужен простой диапазон с одним шагом:

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

еще

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

см. здесь для получения дополнительной информации.

Вот еще один es6 реализация ассортимента

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

Немного краткого, инклюзивного веселья на es6. Демонстрация кода

      var rng=(s,e=null,d=1)=>{
  if(e==null)var[s,e]=[0,s]//missing e? s is e
  if(s>e)d=d<0?d:-d//s,e backwards? might flip sign
  return[...Array(((e-s)/d+1) << 0).keys()].map(x=>d*x+s)
}

rng(3) -> [0,1,2,3]
rng(0,2) -> [0,1,2]
rng(4,6) -> [4,5,6]
rng(3,9,3) -> [3,6,9]
rng(10,27,5) -> [10,15,20,25]
rng(7,null,2) -> [0,2,4,6]
rng(3,-2) -> [3,2,1,0,-1,-2]
rng(3,-2,-2) -> [3,1,-1]
rng(3,-2,2) -> [3,1,-1]
rng(42,42) -> [42]

(Удалить+1из Array(), поскольку это полезно/совместимо.)

Нет, нет, но вы можете сделать один.

Я неравнодушен к поведению диапазона в Python3. Ниже вы найдете реализацию JavaScript в диапазоне ():

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.

      const range = function*(start, stop, inclusive=false) {
    let dx = Math.sign(stop - start);
    if (inclusive) stop += dx;
    for (let x = start; x !== stop; x += dx) yield x;
}

const arange = (start, stop, inclusive) => [...range(start, stop, inclusive)];

Простой подход в Typescript без проверки ошибок. Вверх и вниз вкл. шаг.

      const range = (start: number, end: number | null = null, step: number = 1): number[] =>
  [...Array(end === null ? start : Math.abs(end - start)).keys()]
    .filter((n: number): boolean => n % step === 0)
    .map((n: number): number => (end === null ? n : end < start ? Math.max(start, end) - n : n + start));

То же самое в Javascript ES6

      const range = (start, end = null, step = 1) =>
  [...Array(end === null ? start : Math.abs(end - start)).keys()]
    .filter((n) => n % step === 0)
    .map((n) => (end === null ? n : end < start ? Math.max(start, end) - n : n + start));
      /**
 * range generator
 *
 * @param {Integer} start - Optional. An integer specifying at which position to start. Default is 0
 * @param {Integer} stop  - Required. An integer specifying at which position to stop. Excluded.
 * @param {Integer} step  - Optional. An integer specifying the incrementation. Default is 1
 */

function* range (start, stop, step = 1) {

  if (arguments.length === 1) { [start, stop] = [0, start]; }

  if (![start, stop, step].every(Number.isInteger)) { throw new TypeError('range needs integer arguments'); }

  if ((start - stop) * step >= 0) { return []; }

  let check = start > stop ? (a, b) => a > b : (a, b) => a < b;
  while (check(start, stop)) { yield start, start += step; }
}

console.log([...range(4)]);
console.log([...range(2, 4)]);
console.log([...range(1, 4, 2)]);
console.log([...range(-4, -1, 1)]);
console.log([...range(10, 4, -2)]);
console.log([...range(-1, -4, -1)]);

Вот как я это делаю

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

выход

0
1
2
3
4
Другие вопросы по тегам