You're viewing the readable version of this site. The interactive extras (search, diagrams, read-aloud) need JavaScript and a current browser. Enable JavaScript; if it is already enabled, update your browser.

Notes · Archive

evergreen

Implementing a Pythonic range() function in JavaScript

A Python-style range() helper instead of typing out a sequence by hand.

· · 4 min read

javascript, python, archive

Cite this
APA
Mangalapilly, Y. J. (2009, July). Implementing a Pythonic range() function in JavaScript. Saṃhitā Notes. https://yesudeep.com/blog/pythonic-range-in-javascript/
BibTeX
@online{mangalapilly2009implementing,
  author  = {Yesudeep Jose Mangalapilly},
  title   = {Implementing a Pythonic range() function in JavaScript},
  journal = {Sa\d{m}hit\=a Notes},
  year    = {2009},
  month   = {July},
  url     = {https://yesudeep.com/blog/pythonic-range-in-javascript/},
  urldate = {2026-07-09},
}
Plain
Yesudeep Jose Mangalapilly. “Implementing a Pythonic range() function in JavaScript.” Saṃhitā Notes, 2009. https://yesudeep.com/blog/pythonic-range-in-javascript/.
RIS
TY  - ELEC
AU  - Mangalapilly, Yesudeep Jose
TI  - Implementing a Pythonic range() function in JavaScript
T2  - Saṃhitā Notes
PY  - 2009
UR  - https://yesudeep.com/blog/pythonic-range-in-javascript/
Y2  - 2026-07-09
ER  - 

Note

Originally published on my old WordPress blog in 2009. Preserved here with its original date; the original is still online. A 2026 note: everything here is a fossil. "JavaScript 1.7" was Mozilla's own dialect (the snippets needed <script type"application/javascript;version=1.7">= in Firefox), not the standards committee's — array comprehensions came from the abandoned ES4 draft, were never standardized, and were removed from Firefox in 2018 along with legacy generators. Modern JavaScript writes this as Array.from({length: n}, (_, i) => start + i * step) or a function* generator. Refreshed in July 2026 with captions and a small diagram to make the historical syntax path explicit.

Generators produce a stream of values on demand, replacing eager list allocation with lazy evaluation. — Guido van Rossum, PEP 255 — Simple Generators (2001)

  • Evolution from Firefox JS 1.7 array comprehensions to ES6 generators and Array.from.
  • The memory difference between eager array allocation and lazy generator sequence evaluation.
  • How standard JavaScript handles numeric sequence generation.
The range idea survived; the syntax changed. The old post walks from literal lists to Mozilla-only generator syntax; modern JavaScript keeps the eager/lazy distinction but spells it with standard tools.

Creating a JavaScript array of say, years 2009 to 2050, can be naively done as follows:

var years = [2009, 2010, 2011, 2012, ... 2050];

The literal list. It works, but the range is hand-written.

If you're lazier (and I don't mean that's a bad thing; Perl aficionados admire the virtue of being lazy), you'd do something along the lines of:

var years = [];
for (var year = 2009; year < 2051; year++){
    years.push(year);
}

The manual range loop. Each year is pushed into the array.

And you wouldn't give it much thought. You'd throw that piece into your code and chug along like a good rabbit. JavaScript 1.7 (designed at Mozilla by Brendan Eich) introduced a cool new feature called an array comprehension. I smell Python (created by Guido van Rossum). Python has a similar construct, called a list comprehension, that must have been some source of inspiration for the JavaScript standardizing committee. Anyway, here's what an array comprehension allows you to do:

var years = [year for each (year in range(2009, 2051))];

The JavaScript 1.7 comprehension. range() supplies the values the syntax consumes.

Sweet. One line that does all of that. But wait. JavaScript doesn't come with a range() method, so you can very naively implement one using another cool new feature that comes with JavaScript 1.7: generators. We'll get into much more detail about those in another article perhaps, but here's an oversimplified explanation about what they are. They're your usual functions with an orange-y twist. You can pause a generator function right in the middle using the yield keyword and have it give you a value, finish whatever you want to do with that value and ask that generator function to resume where it left off possibly to yield another value to you or run to completion.

function range(begin, end){
    for (let i = begin; i < end; ++i){
        yield i;
    }
}

A generator-backed range. Each value is yielded on demand.

I cannot possibly take credit for that piece of code because I simply lifted it from the Mozilla Developer Wiki. You may be wondering, why would you ever want to use a generator to do that? Well, the real difference between a generator and an ordinary function that returns a list is the very efficient use of memory. Generators don't have to return an entire list which may be so long your end-user might simply quit using your application. They just return (more correctly, yield) as much as you need and let you go along your way. Now, generators are shiny and all, but there's one little problem. What about when you really need that list of numbers, say from 20 to 400? That's when you would do this:

/**
 * Behaves just like the python range() built-in function.
 * Arguments:   [start,] stop[, step]
 *
 * @start   Number  start value
 * @stop    Number  stop value (excluded from result)
 * @step    Number  skip values by this step size
 *
 * Number.range() -> error: needs more arguments
 * Number.range(4) -> [0, 1, 2, 3]
 * Number.range(0) -> []
 * Number.range(0, 4) -> [0, 1, 2, 3]
 * Number.range(0, 4, 1) -> [0, 1, 2, 3]
 * Number.range(0, 4, -1) -> []
 * Number.range(4, 0, -1) -> [4, 3, 2, 1]
 * Number.range(0, 4, 5) -> [0]
 * Number.range(5, 0, 5) -> []
 *   Number.range(5, 4, 1) -> []
 * Number.range(0, 1, 0) -> error: step cannot be zero
 * Number.range(0.2, 4.0) -> [0, 1, 2, 3]
 */
Number.range = function() {
  var start, end, step;
  var array = [];

  switch(arguments.length){
    case 0:
      throw new Error('range() expected at least 1 argument, got 0 - must be specified as [start,] stop[, step]');
      return array;
    case 1:
      start = 0;
      end = Math.floor(arguments[0]) - 1;
      step = 1;
      break;
    case 2:
    case 3:
    default:
      start = Math.floor(arguments[0]);
      end = Math.floor(arguments[1]) - 1;
      var s = arguments[2];
      if (typeof s === 'undefined'){
        s = 1;
      }
      step = Math.floor(s) || (function(){ throw new Error('range() step argument must not be zero'); })();
      break;
   }

  if (step > 0){
    for (var i = start; i <= end; i += step){
      array.push(i);
    }
  } else if (step < 0) {
    step = -step;
    if (start > end){
      for (var i = start; i > end + 1; i -= step){
        array.push(i);
      }
    }
  }
  return array;
}

The eager Number.range() helper. It returns a full array matching Python's stop-exclusive shape.

And you could now simply do:

var years = Number.range(2009, 2051);

Using Number.range(). One call names the inclusive year span.

to get a list of years from 2009 to 2050 (inclusive).

Lessons

  • Eager array generation allocates all NN elements in heap memory upfront (O(N)O(N) space), whereas lazy generators compute elements on demand in O(1)O(1) space.
  • Proprietary language extensions (like Firefox's JavaScript 1.7 array comprehensions) introduce lock-in risk; web standards favor universal ECMAScript specifications.
  • ES6 standardized iteration protocols via function* generators and Array.from({ length: n }, mapFn), making non-standard range hacks obsolete.

Practice

References

  1. Guido van Rossum, Neil Schemenauer. “PEP 255: Simple Generators.” Python Software Foundation, 2001. — the proposal that introduced the yield keyword and lazy sequence generation
  2. Mozilla Foundation. “New in JavaScript 1.7: Iterators and Generators.” Mozilla Developer Network, 2006. — early Firefox-specific generator and comprehension implementation
  3. ECMA International. “ECMAScript 2015 Language Specification: Iteration Statements.” ECMA Standard 262, 2015. — standardized ES6 generators and iterator protocol replacing JS 1.7 array comprehensions

How to cite

APA
Mangalapilly, Y. J. (2009, July). Implementing a Pythonic range() function in JavaScript. Saṃhitā Notes. https://yesudeep.com/blog/pythonic-range-in-javascript/
BibTeX
@online{mangalapilly2009implementing,
  author  = {Yesudeep Jose Mangalapilly},
  title   = {Implementing a Pythonic range() function in JavaScript},
  journal = {Sa\d{m}hit\=a Notes},
  year    = {2009},
  month   = {July},
  url     = {https://yesudeep.com/blog/pythonic-range-in-javascript/},
  urldate = {2026-07-09},
}
Plain
Yesudeep Jose Mangalapilly. “Implementing a Pythonic range() function in JavaScript.” Saṃhitā Notes, 2009. https://yesudeep.com/blog/pythonic-range-in-javascript/.
RIS
TY  - ELEC
AU  - Mangalapilly, Yesudeep Jose
TI  - Implementing a Pythonic range() function in JavaScript
T2  - Saṃhitā Notes
PY  - 2009
UR  - https://yesudeep.com/blog/pythonic-range-in-javascript/
Y2  - 2026-07-09
ER  - 

Annotations

Thank you — your note is held for review and will appear once approved.

Thank you — your note is published.

Please sign in below to leave a note.