7

I have two numbers, min and max, and I want to create an array that contains all number between them (including min and max).

The most obvious approach is to use a for loop for this, and push the single values onto an array. Nevertheless, this seems to be a quite naive approach, i.e. it's imperative programming.

Now I was thinking of how to create such an array in a more functional style. Basically, something such as the reverse of a reduce function: Instead of reducing an array to a number, building up an array from two numbers.

How could I do this? What is a functional approach to solve this problem?

Basically, I'm thinking of something such as 10..20 in some other languages. What's the most elegant equivalent for this in JavaScript?

Golo Roden
  • 140,679
  • 96
  • 298
  • 425

6 Answers6

10

Inspired by this

var min = 3, max = 10;
var x = Array.apply(null, {length: max + 1}).map(Number.call, Number).slice(min);
console.log(x);
// [ 3, 4, 5, 6, 7, 8, 9, 10 ]

The optimum version

var min = 3, max = 10;
var x = Array.apply(null, {length: max + 1 - min}).map(function(_, idx) {
    return idx + min;
});
console.log(x);
// [ 3, 4, 5, 6, 7, 8, 9, 10 ]
Community
  • 1
  • 1
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
5

A one line way. Inspired by How to create an array containing 1...N.

Array.from({length: max-min+1}, (_, i) => i + min);

Initiate an array from a computed length based on max and min. Then map each index value by a function which is "index + min". The reference is here.

Example:

const min = 12;
const max = 20;
const arr = Array.from({length: max-min+1}, (_, i) => i + min);
juminet
  • 437
  • 1
  • 5
  • 12
4

You can think of a "functional" definition of range:

range(low, hi) = [], if low > hi
range(low, hi) = [low] (+) range(low+1,hi), otherwise,

which leads to the JS definition:

function range(low,hi){
  function rangeRec(low, hi, vals) {
     if(low > hi) return vals;
     vals.push(low);
     return rangeRec(low+1,hi,vals);
  }
  return rangeRec(low,hi,[]);
}
Random Dev
  • 51,810
  • 9
  • 92
  • 119
Mau
  • 14,234
  • 2
  • 31
  • 52
  • 1
    I think, you can remove one more line and do `return rangeRec(low+1,hi, vals.concat(low));` – thefourtheye Mar 18 '14 at 12:39
  • 2
    @thefourtheye: Yes, that would be much more functional (and you should move `rangeRec` outside of `range` then), but creating a new array on each iteration slows it down dramatically (`O(n²)` runtime) – Bergi Mar 18 '14 at 12:50
1

If you have harmony generators you can use this:

function* range(lorange,hirange){
  var n = lorange;
  while (n <= hirange){
    yield n++;
  }
}

rval= range(3,6);

Now you can :

  1. Use the for-of comprehension for iterators as substitute of array

    for (i of rval)
    console.log(i);
    
    3
    4
    5
    6
    
  2. Or you can use it to create an array like you want

    rarray = [];
    for (i of rval)
    rarray.push(i);
    
user568109
  • 47,225
  • 17
  • 99
  • 123
  • The idea is great :-)). Unfortunately, I need such an ranged array to explain what generators are about ;-). – Golo Roden Mar 18 '14 at 12:01
  • @GoloRoden This is a good substitute for the 10..20 range interpolation. It is like the xrange in python 2.7 which returns elements only when needed. – user568109 Mar 18 '14 at 12:43
1

Might be late to the party, but, based on this (es6)

const rangexy = (start, end) => Array.from({length: (end+1 - start)}, (v, k) => k + start);
aazev
  • 86
  • 5
0

Here is good exmaples.

const range = (min, max) => [...Array(max - min + 1).keys()].map((i) => i + min);
TiiGRUS
  • 85
  • 1
  • 4