Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6/7 equivalent to underscore’s range function

Is there any ECMAScript 6/7 equivalent to underscore’s range function?

In underscore:

_.range(startPage, endPage + 1);

In ES2015:

Array.from(Array(n), (_, i) => x + i)

Not sure how the ES2015 version works. I would like to know how range in ecmascript of javascript works

like image 958
Karty Avatar asked Nov 08 '17 21:11

Karty


2 Answers

The idea is to create an array of length end - start + 1, and then fill it with the relevant numbers using Array#from.

The Array.from() method creates a new Array instance from an array-like or iterable object.

In this case Array#from needs an object with the length property. Using Array(n) creates such an object (array). You can also use { length: n } directly. In this case n = Math.abs(end - start) + 1.

Array#from accepts a mapFn callback, a function that can transform the iterated value. The function receives 2 params - the values (which we can ignore in this case), and the index (0 based). Adding start to the current index will create the numbers in the range.

const range = (start, end) => Array.from(
  Array(Math.abs(end - start) + 1), 
  (_, i) => start + i
);

console.log(range(5, 10));
console.log(range(-10, -5));
console.log(range(-5, 10));

This version will handle reverse range as well (large to small) as well:

const range = (start, end) => {
  const inc = (end - start) / Math.abs(end - start);
  
  return Array.from(
    Array(Math.abs(end - start) + 1), 
    (_, i) => start + i * inc
  );
};

console.log(range(5, 10));
console.log(range(-5, -10));
console.log(range(10, -5));
like image 131
Ori Drori Avatar answered Sep 18 '22 17:09

Ori Drori


Note that the following implementation does not allow for lazy-generated lists:

Array.from(Array(n), (_, i) => x + i)

Imagine that you need a list of 1M numbers:

range(1, 1000000);

Are you going to consume all of them? Maybe not, yet all the numbers have been generated already and they probably left a non-trivial footprint on your memory.

It would be nice if we could get them one by one on demand.

It turns out we can do exactly that with a generator:

function* range(start, end, step = 1) {
  for (let value = start; value < end; value += step) {
    yield value;
  }
}

for (let x of range(1, Infinity)) {
  if (x > 10) {
    break;
  }
  console.log(x);
}

Did you notice the range(1, Infinity) bit?

In a classic implementation where all the numbers are generated in advance, this code wouldn't even run as you would get stuck in an infinite number-generating loop.

like image 37
customcommander Avatar answered Sep 21 '22 17:09

customcommander