0

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!

2
  • In JavaScrip an array is (also) an object whose keys are the array indexes. In fact typeof [1, 2, 3] will return 'object' Commented Dec 14, 2021 at 12:05
  • 2
    @secan - While that's true, the keys method on an array is fundamentally different from the Object.keys method typically used with (non-array) objects. It doesn't return an array of the object's own properties, it returns an iterator for the valid indexes of the array (even if they're unpopulated, which the above relies on). Commented Dec 14, 2021 at 12:23

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

This is marvelous, thanks so much! So, in a cheaty way, can I say that the keys in the array are the indexes?
@jampamatos - You're not even cheating, keys is defined as providing an iterator for the valid indexes of the array.
2

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));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.