I was trying to generate some random data using map. To my surprise, I couldn't figure out why this code isn't working.
Consider the following snippet which works as expected:
const empty = [undefined, undefined];
const rand = empty.map(item => Math.random());
Output: [0.4774752874308936, 0.8482276976659398]
I tried to simplify a bit and do the following
const rand = Array(2).map(item => Math.random())
Output: [undefined × 2]
I cannot understand why this is happening. Clearly, both arrays generated by Array(n) and [] are typical arrays and have all prototype methods.
Array(2) instanceof Array
true
[undefined, undefined] instanceof Array
true
Array.isArray(Array(2))
true
Array.isArray([undefined, undefined])
true
Can someone point out where I am going wrong here?
Array(2)
gives you an empty array with a length of 2
. JavaScript arrays are inherently sparse (they can have holes in them, because they aren't really arrays at all¹), and that's what Array(2)
gives you. It looks like this:
+−−−−−−−−−−−−−−+ | (array) | +−−−−−−−−−−−−−−+ | length: 2 | +−−−−−−−−−−−−−−+
whereas your [undefined, undefined]
array looks like this:
+−−−−−−−−−−−−−−+ | (array) | +−−−−−−−−−−−−−−+ | length: 2 | | 0: undefined | | 1: undefined | +−−−−−−−−−−−−−−+
map
, forEach
, and most (but not all) of the related methods on Array.prototype
only loop through the actual entries of a sparse array. Since the array returned by Array(2)
doesn't have any actual entries, your callback is never being called.
ES2015 added Array#fill
(and it can be shimmed), which you can use to fill an array:
const rand = Array(2).fill().map(Math.random)
console.log(rand);
(Note that as Me.Name pointed out we don't need item => Math.random
, we can call Math.random
directly; it doesn't use this
or its arguments (spec).)
There's also this trick for creating a filled array with undefined
s in it:
const empty = [...Array(2)]:
...which you could apply like this if you didn't want fill
:
const rand = [...Array(2)].map(Math.random);
console.log(rand);
Ori Drori points out that we can do
Array.from({length: 2}, Math.random);
e.g.:
const rand = Array.from({length: 2}, Math.random);
console.log(rand);
And Nina Scholz adds the classic, ES5-compatible:
Array.apply(null, {length: 2}).map(Math.random);
var rand = Array.apply(null, {length: 2}).map(Math.random);
console.log(rand);
¹ (That's a post on my anemic little blog)
You could look into the specs of Array#map
:
It is not called for missing elements of the array (that is, indexes that have never been set, which have been deleted or which have never been assigned a value).
You could use Array.apply for getting an iterabel array with map.
console.log(Array.apply(null, { length: 2 }).map(Math.random));
That's because you assume that Array(2)
is interpreted by the JavaScript engine somewhat like this: "Create an array object with two entries initialized to a default value, say 0 or undefined
".
Actually, the JavaScript engine creates a new object and sets its length
property to 2. It does not have any real content.
Check this very simple example:
var arr = new Array(2);
console.log(arr);
If you run it and check the console you're going to see something like this:
Array[2] => {
length: 2,
__proto__: Array[0]
}
No contents, no placeholders. Just an empty array with length
initialized to 2.
That is why your arrow function never gets called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With