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:
- It creates an empty (sparse) array of the appropriate length (
Array(max - min 1)
). - It gets an iterator for the valid indexes of that array (
.keys()
). Valid indexes in an arrays are the numbers0
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 thekeys
iterator returns numbers because we typically use numbers to index into arrays and that's the more useful value to provide.) - It spreads the elements from that iterator out into an array (
[...]
). - 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));