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

96

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

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

array = new Array();
for (i = 0; i < specified_len; i++) {
    array[i] = i;
}
Clwen
источник
1
@clwen: К сожалению, нет, но взгляните на мой код - я написал функцию, которая предназначена для имитации range()работы в Python, чтобы вы могли ее использовать. В JavaScript такой функции нет, но есть плагины для разных фреймворков, например, Rangeкласс для MooTools .
Tadeck

Ответы:

89

Нет , его нет, но сделать можно .

Реализация Python в JavaScript 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(), вы можете использовать вышеупомянутое решение.

Тадек
источник
4
возможно, пара дополнительных защитных проверок - убедитесь, что все переданные аргументы приводятся к числам, и убедитесь, что stopоно больше start(и поменяйте их местами, если нет).
Russ Cam
1
@RussCam: Спасибо, что указали на это. Я не добавлял защитные проверки для типов и т. Д., Но реализовал обратный порядок элементов - теперь он работает точно так же, как и аналог Python, когда последний параметр является отрицательным целым числом.
Tadeck
@RussCam: приведение start >= stopк пустому массиву необходимо, если цель действительно имитировать диапазон Python. И я бы сказал, что в любом случае это более интуитивно понятно.
1
@delnan: проверка может быть более сложной, поскольку простой start >= stopфункции недостаточно, чтобы она работала как range()в Python. Я обновил свой ответ.
Tadeck
@delnan - я не знаком с реализацией Python. Я предполагаю, что если он будет использоваться только людьми, знакомыми с реализацией Python, имеет смысл подражать ему :)
Russ Cam
125

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

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

Из комментария bigOmega , это можно сократить, используя синтаксис Spread :

let range = n => [...Array(n).keys()]
Уилл Эдигер
источник
41
Более простая версия этогоlet range = n => [...Array(n).keys()]
bigOmega
2
@BharathRaja отлично, спасибо! Почему не const? Я постараюсь использовать const везде, где смогу :) как последний аналог Java;)
Kjellski
Вы правы, я стараюсь использовать constкак можно больше, но здесь это будет просто ссылка на массив, поэтому массив все равно будет редактируемым: 'D
bigOmega
1
@bigOmega constбудет применяться к самой функции, а не к ее возвращаемому значению. Вероятно, вы не хотите каким-либо образом изменять функцию.
Соломон Учко
7
Этот ответ не учитывает начальный индекс и возможность увеличения размера шага.
Fluous
29

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


В дополнение к тому, что уже было сказано, Javascript 1.7+ обеспечивает поддержку итераторов и генераторов, которые можно использовать для создания ленивой, экономичной по памяти версии range, похожей на xrangePython2:

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
Георг
источник
1
+1 Отличная идея! Не могли бы вы реализовать также stepаргумент и проверить его на значениях из моего ответа ? Ваш ответ отлично подходит для приложений, для которых у нас есть очень специфические браузеры (он не будет работать в Google Chrome, Safari и версии IE до 9: stackoverflow.com/a/2209743/548696 ).
Tadeck
@Tadeck: по иронии судьбы, недавно я задал очень похожий вопрос , зацените - там есть хорошие ответы. Кстати, ваш код не прошел мой тест; (
georg
Не могли бы вы поделиться данными тестирования и ожидаемыми результатами? Я был бы рад его улучшить, но мои тесты проходят на 100%. Вы говорите, что приведенный мной код неправильно проанализирован сценарием, который вы разместили в этом вопросе: stackoverflow.com/q/12173856/548696 ?
Тадек
@Tadeck: да ладно. Я тестировал срезы, и ваш код предназначен для диапазонов.
georg 09
1
У Python исключеноrange "последнее" значение (поэтому его нужно использовать вместо в приведенном выше коде)>=>
Pac0
23

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

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

Чтобы использовать его в цикле for, вам понадобится цикл 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
источник
Почему бы не использовать параметры по умолчанию, если вы используете ES6?
Амин НАИРИ
@Gradiuss, который будет работать для stepпараметра, но если stopон не передан, оба startи stopдолжны быть изменены. Я буду обновлять его
janka102
2
WTG, это лучшая реализация из представленных на данный момент. Он использует генераторы (хорошо, поскольку нет необходимости хранить всю последовательность в памяти) и очень лаконичен.
Лучио Пайва
undefined - это указатель на объект вроде null. можно сравнить с 3 знаками равенства, например: if (stop === undefined) {3 знака равенства - сравнение без автоматического преобразования. сравнить как и сравнить тип. 2 знака равенства сравниваются с автоматическим преобразованием в другой тип.
Шимон Дудкин
20

Порт rangeфункции из Python 2 предоставляется служебными библиотеками underscore.js и 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);
=> []
Марк Эмери
источник
15

Может быть достигнуто путем присоединения итератора к 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».

mcha
источник
7
Это действительно круто, но переопределение прототипов слишком хрупко: '(
m0meni
8

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

let range = (start, end) => Array.from(Array(end + 1).keys()).slice(start);
Дмитрий Михайлов
источник
7

Ну вот.

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

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]
RightSaidFred
источник
5

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

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

работает как

> range(4)
[0, 1, 2, 3]
bigOmega
источник
2
var range = n => Array(n).fill().map((e, i) => i);
Вален
и зачем говорить «размер x», когда вы на самом деле использовали в nкачестве имени параметра
Вален
5

Ниже приводится естественная адаптация функции 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.

le_m
источник
Согласовано. Хотя другие ответы по-своему элегантны, этот подход и функциональность гораздо более питоничны.
Travis Clarke
4

Дальнейшее уточнение с параметрами по умолчанию 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
источник
Вот как на самом деле должен выглядеть диапазон.
Конрад Линковски
4

pythonicrangeЛучше всего имитирует поведение Python, используя JS 'generators ( yield), поддерживая как варианты использования , так range(stop)и range(start, stop, step)варианты использования. Кроме того, pythonic«s rangeфункция возвращает Iteratorобъект , похожий на Python , который поддерживает 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

Keyvan
источник
3

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

// 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"]

Илиаст
источник
2

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

Радагаст
источник
2

Есть ли в 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]
Мэтт Кохрейн
источник
1

По-прежнему нет встроенной функции, эквивалентной 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количество объектов.

Стив Браунли
источник
1

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

function range(start, end) {
  return Array.from(Array(end||start).keys()).slice(!!end*start)
}
Роб Квасовски
источник
0

Вот еще одна 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}`)
}

mrFunkyWisdom
источник
Что, если мы хотим сгенерировать массив от -20до -30?
Амин НАИРИ
Хм, этот сгенерирует массив от -30 до -20, но его можно легко изменить, чтобы включить обратное свойство, если хотите. Я отредактирую ответ выше, чтобы включить его
mrFunkyWisdom
0

Нет, его нет, но сделать можно.

Я неравнодушен к поведению диапазона Python3. Ниже вы найдете реализацию Python range () в 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.

Elayira
источник
0

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

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

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

Н Джел Окойе
источник
0

Есть ли в 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...
}
sndmnn
источник
0

Вариант для 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

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

Отто
источник
-1

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

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

выход

0
1
2
3
4
Рики
источник
Это не поддерживает шаги.
Марк Стосберг,