Home > Net >  Understanding a range function in JavaScript
Understanding a range function in JavaScript

Time:12-15

So, I'm new to programming and I am having a bit of a trouble here. See, I wanted to write a small range function, like range(a,b) that would return an array with all numbers between a and b. So I googled and found this one:

const range = (min, max) => [...Array(max - min   1).keys()].map(i => i   min);

This works perfectly fine, but I'm having a bit of trouble undertanding it, especially with the .keys() part. I thought .keys() was an Object function, that would return the key of a key/value pair of an object, but here it seems to me that it's been used in an Array.

What am I understanding wrong here?

Appreciate the help!

CodePudding user response:

Arrays also have a keys method and it's central to this range function working as it should.

Here's how that works:

  1. It creates an empty (sparse) array of the appropriate length (Array(max - min 1)).
  2. It gets an iterator for the valid indexes of that array (.keys()). Valid indexes in an arrays are the numbers 0 through the length of the array minus one. (Technically, in specification terms, the indexes of arrays are string property names like "0", "1", etc., but the keys iterator returns numbers because we typically use numbers to index into arrays and that's the more useful value to provide.)
  3. It spreads the elements from that iterator out into an array ([...]).
  4. It creates a new array by mapping each value in the array from #3, adding the min value to them (.map(i => i min)).

Note that this creates multiple intermediate arrays and other objects. It's unlikely to matter, but it can be done much more efficiently. For instance, with Array.from as shown by Nina, or even just a simple loop:

const range = (min, max) => {
    const result = [];
    for (let n = min; n < max;   n) {
        result.push(n); // Or: `result[result.length] = n;` if you want to
                        // avoid the function call
    }
    return result;
};

const range = (min, max) => {
    const result = [];
    for (let n = min; n < max;   n) {
        result.push(n);
    }
    return result;
};
console.log(range(5, 10));

That's not nearly as cool-looking, though. :-D

But, I probably wouldn't have range return an array at all unless I know for sure that's the end product I'm always going to need. Instead, I'd have it return an iterator:

const range = function*(min, max) {
    for (let n = min; n < max;   n) {
        yield n;
    }
};
        
for (const value of range(0, 10)) {
    console.log(value);
}
.as-console-wrapper {
    max-height: 100% !important;
}

You can always spread it out into an array (or use it with Array.from) if you need an array.

CodePudding user response:

Arrays have keys as well and returns an iterator for returning keys. This requires a spreading into an array or use a function which takes iterables and returns an array.

For this purpose Array.from is made - and this has a mapping function as well.

Instead of builing more than one array, you could take Array.from directly with an object with a length property and a mapping function.

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

console.log(...range(7, 9));

  • Related