6

I've recently discovered that mapping an uninitialised array doesn't seem to work as I would expect. With this code:

function helloMap(value, index) {
    return "Hello " + index;
}

console.clear();

var initialArray = Array.apply(null, new Array(5));

console.log("Array initialised with apply:");
console.log(initialArray);
console.log(initialArray.map(helloMap));

var normalArray = new Array(5);

console.log("Array constructed normally");
console.log(normalArray);
console.log(normalArray.map(helloMap));
.as-console-wrapper {
  max-height: 100% !important;
}

I get different results despite the first output for each array being [undefined, undefined, undefined, undefined, undefined].

The fact that I get different results implies that the undefined in these 2 arrays are in fact different. In the first I suspect that the array has 5 items in, each one is undefined. In the second the array is 5 items long but there is nothing, not even undefined in there...

It's a bit confusing.

Can someone explain it to me?

Just a student
  • 10,560
  • 2
  • 41
  • 69
Roaders
  • 4,373
  • 8
  • 50
  • 71
  • 1
    small note...console.log(initialArray.map(helloMap); should be console.log(initialArray.map(helloMap)); missing paren and my edit queue is full so i can't just edit – John Vandivier Apr 06 '17 at 13:08
  • 3
    related to: "Why does Array.apply(null, [args]) act inconsistently when dealing with sparse arrays?" [Link here.](http://stackoverflow.com/questions/22949976/why-does-array-applynull-args-act-inconsistently-when-dealing-with-sparse-a) – John Vandivier Apr 06 '17 at 13:13
  • 2
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array - "this implies an array of arrayLength empty slots, not slots with actual undefined values" – naortor Apr 06 '17 at 13:15
  • @JohnVandivier Never edit code blocks, you could fix a typo that was causing the problem. – Feathercrown Apr 06 '17 at 13:17
  • @Feathercrown trust me I had verified it was not causing the issue before saying so – John Vandivier Apr 06 '17 at 13:19
  • @JohnVandivier Ah, never mind then. That's fine. :) – Feathercrown Apr 06 '17 at 13:21
  • [Read the first paragraph of the "Description" section.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) – Pointy Apr 06 '17 at 13:22
  • Reference: http://stackoverflow.com/questions/40752434/how-to-tell-between-undefined-array-elements-and-empty-slots – Tim Grant Apr 06 '17 at 13:22
  • 1
    instead of `Array.apply(null, Array(length)).map(fn)` or the not working `Array(length).map(fn)` you can use `Array.from({length}, fn)`. Or you wrap that into a function `const array = (length=0, value=undefined) => typeof value === "function"? Array.from({length}, value): Array(+length).fill(value);` – Thomas Apr 06 '17 at 13:26

4 Answers4

5

Array.apply(null, Array(5)) actually fills the array (or array-like object) that you pass as the second argument with the value of the first argument you pass in, as can be seen in the MDN Docs.

new Array(5) is just initializing an array with it's length property set to the argument of 5. Again, as can be seen in the MDN docs:

If the only argument passed to the Array constructor is an integer between 0 and 232-1 (inclusive), this returns a new JavaScript array with its length property set to that number (Note: this implies an array of arrayLength empty slots, not slots with actual undefined values).

Just a student
  • 10,560
  • 2
  • 41
  • 69
jeffdill2
  • 3,968
  • 2
  • 30
  • 47
3

According to MDN Array.prototype.map

map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values, including undefined. 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).

Both the arrays are different in the way Array.map executes the callback. Since the array in second scenario doesn't have indexes map is returning empty

REASON

The answer lies in Array constructor

In the first scenario you are passing an array with length 5 to Array constructor which will index the array based on the length but in the second scenario you are just using the array with length 5

You will get to know the difference when you run Object.keys(initialArray) with Object.keys(normalArray)

Try checking the below example.

function helloMap(value, index) {
    return "Hello " + index;
}

console.clear();

var initialArray = Array.apply(null, new Array(5));

console.log("Array initialised with apply:");
console.log(initialArray);
console.log(initialArray.map(helloMap));

var normalArray = new Array(5);

console.log("Array constructed normally");
console.log(normalArray);
console.log(normalArray.map(helloMap));

//DIFFERENCE
console.log("Initial Array: "+Object.keys(initialArray));
console.log("Normal Array: "+Object.keys(normalArray));
.as-console-wrapper {
  max-height: 100% !important;
}
Gangadhar Jannu
  • 4,136
  • 6
  • 29
  • 49
1

map is not called on any elements of normalArray because there are no elements. Normal array is an array with 5 empty slots.

initialArray has 5 slots filled with undefined

Ken
  • 4,367
  • 4
  • 28
  • 41
1

I think here is your answer, mozilla developers

console.log(normalArray.map(helloMap)); will always fail, because it runs in the context - creates new array but won't work with undefined values.

map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values, including undefined. 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).

loelsonk
  • 3,570
  • 2
  • 16
  • 23