Функція JavaScript, подібна до діапазону Python ()


96

Чи є функція в JavaScript схожа на функцію Python range()?

Я думаю, що повинен бути кращий спосіб, ніж щоразу писати такі рядки:

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

1
@clwen: На жаль, цього немає, але погляньте на мій код - я написав функцію, яка спрямована на емуляцію того, як range()працює Python, так що ви можете ним користуватися. У JavaScript такої функції немає, але є деякі плагіни для різних фреймворків, наприклад Rangeклас для MooTools .
Tadeck

Відповіді:


89

Ні , немає, але ти можеш зробити його .

Реалізація 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(), ви можете скористатися вищезазначеним рішенням.


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
Ця відповідь не враховує стартовий індекс і можливість збільшити розмір кроку.
Річний

29

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


На додаток до того, що вже було сказано, Javascript 1.7+ забезпечує підтримку ітераторів та генераторів, які можна використовувати для створення лінивої, економічної пам'яті версії rangesimlar to 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

1
+1 Чудова ідея! Не могли б ви також реалізувати stepаргумент і перевірити його на значеннях з моєї відповіді ? Ваша відповідь чудова для програм, де ми маємо на увазі дуже конкретні браузери (він не працюватиме в Google Chrome, Safari та IE версії раніше 9: stackoverflow.com/a/2209743/548696 ).
Tadeck

@Tadeck: за іронією долі, я нещодавно задав дуже подібне запитання , перевірте - там є кілька хороших відповідей. До речі, ваш код не проходить мого тесту; (
georg

Не могли б ви поділитися даними тестування та очікуваними результатами? Я був би радий його вдосконалити, але мої тести проходять на 100%. Ви кажете, що код, який я надав, неправильно проаналізований сценарієм, який ви розмістили у цьому питанні: stackoverflow.com/q/12173856/548696 ?
Tadeck

@Tadeck: ніколи. Я протестував фрагменти, і ваш код призначений для діапазонів.
georg

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

Чому б не використовувати параметри за замовчуванням, якщо ви використовуєте ES6?
Amin NAIRI

@Gradiuss , що буде працювати для stepпараметра, але коли stopне минуло, як startі stopповинні бути змінені. Я
оновлю

2
WTG, це найкраща реалізація, представлена ​​на сьогодні. Він використовує генератори (добре, оскільки немає необхідності зберігати всю послідовність у пам'яті) і є дуже стислим.
Lucio Paiva,

undefined - це вказівник на такий об'єкт, як null. можна порівняти з 3-ма знаками рівності, наприклад: if (stop === undefined) {3 дорівнює знаками - це порівняння без автоматичного приведення. порівняти як і тип порівняння. 2 знаки рівності порівняно з автоматичним відливом до іншого бічного типу.
Шимон Дудкін 27.03.18

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"


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]

5

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

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

працює як

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

2
var range = n => Array(n).fill().map((e, i) => i);
Вален

і навіщо говорити "розмір x", коли ви насправді використовували nяк назву параметра
Вален

5

Нижче наведено природну адаптацію функції range () 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]

Він підтримує будь-який аргумент, який можна порівняти 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.


Домовились. Хоча інші відповіді по-своєму елегантні, цей підхід та функціональність набагато пітонічніші.
Тревіс Кларк,

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
}

Ось як справді повинен виглядати асортимент.
Конрад Лінковський

4

pythonicrangeнайкраще імітує поведінку Python, використовуючи генератори JS ( yield), підтримуючи випадки використання range(stop)та range(start, stop, step). Крім того, функція pythonic'' 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


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

Чи є в JavaScript функція, подібна до діапазону Python ()?

Всі наведені тут рішення стосуються діапазону 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}`)
}


Що робити, якщо ми хочемо створити масив від -20до -30?
Амін

Хм, цей генерує масив від -30 до -20, але його можна легко змінити, щоб включити зворотну властивість, якщо хочете. Я відредагую відповідь вище, щоб включити її
mrFunkyWisdom

0

Ні, немає, але ти можеш зробити його.

Я прихильний до поведінки діапазону Python3. Нижче ви знайдете реалізацію JavaScript діапазону 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.


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 ()?

Як відповіли раніше: ні , немає. Але ти можеш зробити свій власний. Я вважаю, що це цікавий підхід для 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 ()):

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

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

Але, хто в здоровому розумі розумі використовує буфер для таких цілей;)


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.