JavaScript "new Array(n)" and "Array.prototype.map" weirdness

81,121

Solution 1

It appears that the first example

x = new Array(3);

Creates an array with a length of 3 but without any elements, so the indices [0], [1] and [2] is not created.

And the second creates an array with the 3 undefined objects, in this case the indices/properties them self are created but the objects they refer to are undefined.

y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it's the same as new Array(3)
y = [,,,];

As map runs on the list of indices/properties, not on the set length, so if no indices/properties is created, it will not run.

Solution 2

I had a task that I only knew the length of the array and needed to transform the items. I wanted to do something like this:

let arr = new Array(10).map((val,idx) => idx);

To quickly create an array like this:

[0,1,2,3,4,5,6,7,8,9]

But it didn't work because: (see Jonathan Lonowski's answer a few answers beneath)

The solution could be to fill up the array items with any value (even with undefined) using Array.prototype.fill()

let arr = new Array(10).fill(undefined).map((val,idx) => idx);

console.log(new Array(10).fill(undefined).map((val, idx) => idx));

Update

Another solution could be:

let arr = Array.apply(null, Array(10)).map((val, idx) => idx);

console.log(Array.apply(null, Array(10)).map((val, idx) => idx));

Solution 3

With ES6, you can do [...Array(10)].map((a, b) => a) , quick and easy!

Solution 4

ES6 solution:

[...Array(10)]

Doesn't work on typescript (2.3), though

Solution 5

From the MDC page for map:

[...] callback is invoked only for indexes of the array which have assigned value; [...]

[undefined] actually applies the setter on the index(es) so that map will iterate, whereas new Array(1) just initializes the index(es) with a default value of undefined so map skips it.

I believe this is the same for all iteration methods.

Share:
81,121

Related videos on Youtube

rampion
Author by

rampion

Mathematician, programmer, and researcher; with interests in algorithmic design, software engineering theory, and massively parallel computing. I'm also a keyboard junkie and an aspiring language nerd.

Updated on November 11, 2021

Comments

  • rampion
    rampion over 2 years

    I've observed this in Firefox-3.5.7/Firebug-1.5.3 and Firefox-3.6.16/Firebug-1.6.2

    When I fire up Firebug:

    var x = new Array(3)
    console.log(x) 
    // [undefined, undefined, undefined]
    
    var y = [undefined, undefined, undefined]
    console.log(y) 
    // [undefined, undefined, undefined]
    
    console.log( x.constructor == y.constructor) // true
    
    console.log( 
      x.map(function() { return 0; })
    )
    // [undefined, undefined, undefined]
    
    console.log(
      y.map(function() { return 0; })
    )
    // [0, 0, 0]

    What's going on here? Is this a bug, or am I misunderstanding how to use new Array(3)?

    • RussellUresti
      RussellUresti about 13 years
      I don't get the same results you see from the array literal notation. I still get undefined instead of 0. I only get the 0 result if I set something like var y = x.map(function(){return 0; });, and I get this for both the new Array() method and the array literal. I tested in Firefox 4 and Chrome.
    • Hashbrown
      Hashbrown over 4 years
      also busted in Chrome, this might be defined in the language, although it makes no sense so I really hope it isnt
  • Lightness Races in Orbit
    Lightness Races in Orbit about 13 years
    It's defined to be broken? It's designed to produce an array of three elements that will always be undefined for ever?
  • Pointy
    Pointy about 13 years
    Yes, that's correct, except for the "forever" part. You can subsequently assign values to the elements.
  • gen_Eric
    gen_Eric about 13 years
    That's why you should use x = [] instead of x = new Array()
  • Martijn
    Martijn about 13 years
    From MDC (emphasis mine): "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; it is not invoked for indexes which have been deleted or which have never been assigned values." In this case, x's values have not explicitly assigned values, whereas y's were assigned, even if it was the value undefined.
  • Trevor Norris
    Trevor Norris over 11 years
    So is it a JavaScript failing that it's impossible to check whether it's an undefined pointer, or a pointer to undefined? I mean (new Array(1))[0] === [undefined][0].
  • David Mårtensson
    David Mårtensson over 11 years
    Well, an array of undefined is different from an array of pointers to undefined objects. An array of undefines would be like an array of null values, [null, null, null] while an array of pointers to undefined would be like [343423, 343424, 343425] poining to null and null and null. The second solutions have real pointers pointing to memory addresses while the first do not point anywhere. If that is a failing of JS is probably a matter o discussion, but not here ;)
  • squid314
    squid314 about 9 years
    @TrevNorris, you can easily test that with hasOwnProperty unless hasOwnProperty itself has a bug: (new Array(1)).hasOwnProperty(0) === false and [undefined].hasOwnProperty(0) === true. In fact, you can do the exact same with in: 0 in [undefined] === true and 0 in new Array(0) === false.
  • Ruhee Jaiswal
    Ruhee Jaiswal about 9 years
    Talking about "undefined pointers" in JavaScript confuses the issue. The term you're looking for is "elisions". x = new Array(3); is equivalent to x = [,,,];, not x = [undefined, undefined, undefined].
  • David Mårtensson
    David Mårtensson about 9 years
    I never claimed that the two are equal, on the contrary, its that they are NOT equal I emphase, that in the first case the array has a size but no content and the mapping function cannot run but in the second there is objects even if they are undefined and map can run as long as you do not try to access the object. OP was confused by the fact that the two rendered the same output in firebug while not behaving the same in the map function.
  • Yann Eves
    Yann Eves about 8 years
    worth noting you don't need to state undefined in the .fill() method, simplifying the code very slightly to let arr = new Array(10).fill().map((val,idx) => idx);
  • Molomby
    Molomby almost 7 years
    Pre-ES6 you can use new Array(10).fill(). Same result as [...Array(10)]
  • ibex
    ibex almost 6 years
    Array(10).fill("").map( ... is what worked for me with Typescript 2.9
  • Admin
    Admin almost 5 years
    With large arrays the spread syntax creates issues so it's better to avoid
  • Admin
    Admin almost 5 years
    Similarly you can use Array.from(Array(10))
  • Magnus
    Magnus about 4 years
    I'm curious. Under the hood (in C++ land, etc.), what is the pointer pointing to if it is an "undefined pointer"? Does javascript allocate a special location in memory that represents "being undefined".
  • Chungzuwalla
    Chungzuwalla almost 4 years
    or [...Array(10).keys()]
  • vsync
    vsync over 3 years
    I know how it works, but please add some explanation, for those who don't
  • Sebastien H.
    Sebastien H. almost 3 years
    I would recommend @eden-landau 's answer, since it's a cleaner way of initializing an array
  • Sebastien H.
    Sebastien H. almost 3 years
    This is the cleanest answer to me.
  • Sebastian Simon
    Sebastian Simon over 2 years
    The terminology is still wrong in this answer. The reason that map doesn’t execute its callback is that new Array(3) doesn’t have an index property up to its length. The point about hasOwnProperty is relevant: map will execute for every entry in Object.keys(array) (or .values or .entries etc.). If that’s empty, map will execute 0 times. This has nothing to do with “pointers”. The reason new Array(3)[0] is undefined is because that’s what non-existent properties produce. Good thing modern debugging tools make this difference more evident. “undefined object” is an oxymoron.
  • Piliponful
    Piliponful about 2 years
    The answer is incorrect
  • Trevor Karjanis
    Trevor Karjanis almost 2 years
    Remember to use return when mapping to an object. :X Array.apply(null, Array(10)).map(() => { return {}; });
  • cstuncsik
    cstuncsik almost 2 years
    @TrevorKarjanis thanks for the hint but feels not relevant. Where do I need an object here?
  • Trevor Karjanis
    Trevor Karjanis almost 2 years
    @cstuncsik You don't. You're returning an number idx. My comment is just to help anyone that followed your answer but needs to map to objects.
  • Phil D.
    Phil D. almost 2 years
    @TrevorKarjanis we're going way off topic here, but the crux of your statement is just how returned object literals work in a arrow functions: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • Trevor Karjanis
    Trevor Karjanis almost 2 years
    @Phil D Yep! I've known that and still make the mistake every once in a while.