5

I'm trying to understand the difference between an 'empty' sparse array (e.g. new Array(3)) and an equivalent 'empty' dense array (array with 3 undefined entries).

I can create an array with 3 undefined values these two ways:

var sparse = new Array(3);
// or
var sparse = [,,,];

var dense = Array.apply(null, Array(3)); // See dense array link below

Dense Arrays

If I do console.log for either of these the result is:

[undefined, undefined, undefined]

If I loop over each array to compare it against the other one they will strictly match:

console.log(sparse.length === dense.length);

// true

for (var i = 0; i < dense.length; i++) {
  console.log(i +':'+ (dense[i] === sparse[i]));
}

// '0:true'
// '1:true'
// '2:true'

However, if I use .forEach (or map, reduce, etc) then the callback will never be called on the sparse array but will be called three times on the dense one:

sparse.forEach(function(val,i){
   console.log(i +':'+ val);
});

// Nothing. No-op.

dense.forEach(function(val,i){
   console.log(i +':'+ val);
});

// '0:undefined'
// '1:undefined'
// '2:undefined'

So my questions are:

  1. If they both have the same length, indexes, and values how is one iterable but the other isn't?
  2. What is the reason for the difference?
  3. What is the best way to determine if an array is sparse or dense?
pseudosavant
  • 7,056
  • 2
  • 36
  • 41
  • This is covered in the EMCAScript specification under .. forEach. Basically it only "runs" for elements that *have* the property (denoted by the index) assigned any value (including undefined). Just because accessing an unassigned index returns undefined does not mean that it is *assigned* undefined. (There are also duplicate questions.) – user2864740 Mar 10 '15 at 21:06
  • 1
    Related: http://stackoverflow.com/questions/11808946/what-is-the-difference-between-undefined-and/11809022#11809022 , http://stackoverflow.com/a/9329476/2864740 – user2864740 Mar 10 '15 at 21:08
  • I saw both of those related questions but I don't feel like they are really asking all of the same questions. For instance what is the rationale for why `[,,,]` and `new Array(2)` are equivalent but `[undefined,undefined]` while looking very similar is actually very different. It is like saying a variable doesn't exist until it explicitly has a value set. – pseudosavant Mar 10 '15 at 21:31
  • I'm more interested in the **why** than the **what**. Like why `0.1 + 0.2 !== 0.3`. The why is far more interesting. – pseudosavant Mar 10 '15 at 21:37
  • But the why .. is already covered. And if meaning "why was it done like that?" then 9/10 times it's a question without a good objective (and real why) answer but rather one that begs specuation .. – user2864740 Mar 10 '15 at 22:14
  • Anyway, "I can create an array with 3 undefined value" is *wrong* which leads to further incorrect conclusions - the linked answers explain why. In reality an array with *no* assigned elements (but a particular length) is being created. Accessing an unassigned element/property (which results in undefined) does *not* mean that such is actually assigned to said element/property. (This last phenomena is why it often "looks" like array created in such a manner contains undefined values when it actually contains *no* values - not even undefined!) – user2864740 Mar 10 '15 at 22:18
  • Another way to look at it is any object (including arrays) can be viewed of a map/sequence of `((prop1, value), (prop2, value), ...)` such that all the property names are unique. When creating an array with `[undefined,undefined]` the result is `(("0",undefined),("1",undefined))` (and implicitly a length). But when creating it as `new Array(2)` it is `()` (and implicitly a length) - that is there are *no* element values present at all. – user2864740 Mar 10 '15 at 22:23
  • I mentioned this in a comment on the answer below but isn't it odd that `[,,,]` is meaningfully different than `[undefined, undefined]`? That is like `var a;` being meaningfully different than `var a = undefined;`. I understand that this is how it is, but I'm just trying to understand why. Why does `[undefined]` create an index but `[,]` doesn't? – pseudosavant Mar 10 '15 at 22:42
  • It doesn't matter if it is "odd". It is what is and it is explained the specification (it explicitly points this out) and it is explained in more depth in other answers. – user2864740 Mar 10 '15 at 23:51

1 Answers1

4
  1. If they both have the same length, indexes, and values how is one iterable but the other isn't?

These functions are explicitly documented to perform this way. Both arrays are iteratable, but forEach/map/etc explicitly skips indices which are not in the array:

var sparse = new Array(3);
var dense = Array.apply(null, Array(3)); // See dense array link belo

1 in sparse; // false
1 in dense; // true

// Sparse can still be iterated over
sparse[100] = 'a'
sparse.forEach(function (n, i) { console.log(n, i) }); // a 100
  1. What is the reason for the difference?

Presumably an explicit decision was made to omit indices which don't exist

  1. What is the best way to determine if an array is sparse or dense?

You can use the following:

function isSparse(array) {
  for (var i = 0; i < array.length; ++i) {
    if (!i in array)
      return true;
  return false;
}

RE: Your comment

There has got be a reason for why [,,,] doesn't have indexes but [undefined, undefined, undefined] does.

There doesn't have to be a reason, that's just the way they're built. One has keys, the other doesn't.

Look:

Object.keys(Array(3)) // => []
Object.keys(['a','b']) // => ["0", "1"]
user229044
  • 232,980
  • 40
  • 330
  • 338
  • There has got be a reason for why `[,,,]` doesn't have indexes but `[undefined, undefined, undefined]` does. – pseudosavant Mar 10 '15 at 21:39
  • There might not be a reason but typically there is, even for the most idiosyncratic stuff. But `[,,,]` being meaningfully different than `[undefined,undefined,undefined]` is like `var a;` being meaningfully different from `var a = undefined;` which would be really odd. – pseudosavant Mar 10 '15 at 22:39
  • 1
    @pseudosavant No, it's like `alert(a)` being meaningfully different than `var a; alert(a)`, which *they are*. The first one raises an error because `a` is not defined, the second one alerts `undefined`. This whole conversation surrounds the difference between sparse and dense arrays, so you **already admit** that they are *different things*, so why are you surprised that they have different behaviors? – user229044 Mar 10 '15 at 23:36
  • I don't want to be argumentative. I just want to dig deeper on this. This is the only place in JS that I can think of where whether you assign an undefined value or just don't assign a value at all where you end up with a meaningfully different result. Calling `function fn(v) {}` like this `fn()` or this `fn(undefined)` is exactly the same. `var a;` is exactly the same as `var a = undefined;`. On the other hand you can't define an object with properties that don't actually exist (e.g. `{,,,,}`) like you can with an array (e.g. `[,,,,]`). – pseudosavant Mar 11 '15 at 17:13
  • @pseudosavant That still isn't equivalent. It doesn't matter how you call `fn(v)`, inside that function, `v` is still declared, and it doesn't matter whether you use `var a` or `var a = undefined`, because `a` is still declared. These are not the same thing as attempting to enumerate properties on an object which **do not exist**. – user229044 Mar 11 '15 at 17:19
  • @pseudosavant If I wrote `var a = {}; Object.keys(a)`, obviously there are no keys, and you wouldn't expect there to be. But if I wrote `var a = {}; a.name = undefined; Object.keys(a)`, then yes, `name` will be in the list of keys for object, even though its value is undefined. But until I actually assign to `name`, the property `name` does not exist inside `a`, and if I iterate over the object's properties, I won't get `name` back out of it. The object doesn't implicitly contain all possible property names just because I can do `a.asdhgaiouwhet` and get back `undefined`. – user229044 Mar 11 '15 at 17:23
  • The same is true of arrays though. `var a = []; Object.keys(a);` won't have keys. `var a = []; a[0] = undefined; Object.keys(a);` will have keys. My curiosity is more about how/why an array literal can be defined like this `[,,,]` as is valid but not have any indexes/properties but you can't do that with something else like an object literal `{,,,}`. It is about why the language was designed this why, not whether it is this way or not. Clearly it is this way, no argument at all. – pseudosavant Mar 11 '15 at 17:32
  • `{,,,}` does not make sense. You cannot have sparse objects. I will say again: There are sparse arrays, and dense arrays. JavaScript supports sparse arrays, and the only difference in behavior is the one we're talking about. If this difference in behavior didnt' exist, JavaScript wouldn't have sparse arrays, it would only have dense arrays. I know this is begging the question, but I don't know how else to answer this. Your question is "Why does JavaScript have sparse arrays", and the only answer I can give is "it does". – user229044 Mar 11 '15 at 17:44
  • FWIW, I already up-voted and accepted your answer. I really don't want this to be an argument, just a discussion. Think about this differently. Why does `0.1 + 0.2 !== 0.3` in JavaScript? Because it uses binary floating point, specifically IEEE-754. So although it doesn't "make sense", there is a specific reason behind why. Very little in the EMCAScript standard is accidental. I'm really curious about the rationale for why the standard was designed such that `[,,,,]` creates a sparse array. – pseudosavant Mar 11 '15 at 18:29