javascriptarrayspropertiessparse-matrix

What is empty inside an array? e.g. [empty × 5]


Sorry for misleading title here, I wasn't able to frame any proper one.

I am confused in array when there is nothing inside them (prints by empty × n) but they have length.
e.g. I create array by const a = [,,,]. This creates an array whose length is 3 but nothing inside it. If i print it in browser console, it prints the following:

enter image description here

What does empty mean here? If I run map or forEach function and try to console something, I get nothing.

Have added some code.

const a = [,,,]

console.log("print a: ",a)
console.log("print a.length: ",a.length)
console.log("print typeof a[0]: ", typeof a[0])
console.log("a.forEach((data, index) => { console.log(data, index) }): ", a.forEach((data, index) => { console.log(data, index) }))
console.log("")


const b = [undefined, undefined, undefined]

console.log("print b: ", b)
console.log("print b.length: ", b.length)
console.log("print typeof b[0]: ", typeof b[0])
console.log("b.forEach((data, index) => { console.log(data, index) }): ", b.forEach((data, index) => { console.log(data, index) }))
console.log("")

console.log("compare a[0] and b[0]: ", a[0] === b[0])

The only thing which differs is when I print a and b (though stackoverflow console prints them same but browser console prints differently) and when I try to loop through the array. Also momentjs isEqual gives them equal (jsfiddle here)

My main doubts are:

I have read about null and undefined array values and have understood it. But for this one, I haven't find anything proper. Most of the search I found were related to const a = [] is an empty array or how to check if array is empty and so on.

So, if someone can explain or give any proper links to read, it will be very helpful.

Please let me know, if I should add anything else.


Solution

  • Intro to sparse arrays

    First a clarification what you've created is called a sparse array. To put it simply, sparse arrays are similar to normal arrays but not all of their indexes have data. In some cases, like JavaScript, this leads to slightly more significant handling of them. Other languages simply have a normal array of fixed length with some values that are "zero" in some sense (depends on what value can signify "nothing" for a specific array - might be 0 or null or "", etc).

    Empty slots

    The empty slot in a sparse array is exactly what it sounds like - slot that is not filled with data. JavaScript arrays unlike most other implementations, are not fixed size and can even have some indexes simply missing. For example:

    const arr = [];   // empty array
    arr[0] = "hello"; // index 0 filled
    arr[2] = "world"; // index 2 filled
    

    You will get an array with no index 1. It's not null, nor it's empty, it's not there. This is the same behaviour you get when you have an object without a property:

    const person = {foo: "hello"};
    

    You have an object with a property foo but it doesn't have, for example, a bar property. Exactly the same as how the array before doesn't have index 1.

    The only way JavaScript represents a "value not found" is with undefined, however that conflates

    Here as an example:

    const person1 = { name: "Alice", age: undefined };
    const person2 = { name: "Bob" };
    
    console.log("person1.age", person1.age);
    console.log("person2.age", person2.age);
    
    console.log("person1.hasOwnProperty('age')", person1.hasOwnProperty('age'));
    console.log("person2.hasOwnProperty('age')", person2.hasOwnProperty('age'));

    You get undefined when trying to resolve age in either case, however the reasons are different.

    Since arrays in JavaScript are objects, you get the same behaviour:

    const arr = [];   // empty array
    arr[0] = "hello"; // index 0 filled
    arr[2] = "world"; // index 2 filled
    
    console.log("arr[1]", arr[1]);
    
    console.log("arr.hasOwnProperty(1)", arr.hasOwnProperty(1));

    Why it matters

    Sparse arrays get a different treatment in JavaScript. Namely, array methods that iterate the collection of items will only go through the filled slots and would omit the empty slots. Here is an example:

    const sparseArray = [];   // empty array
    sparseArray[0] = "hello"; // index 0 filled
    sparseArray[2] = "world"; // index 2 filled
    
    const arr1 = sparseArray.map(word => word.toUpperCase());
    
    console.log(arr1); //["HELLO", empty, "WORLD"]
    
    
    const denseArray = [];     // empty array
    denseArray[0] = "hello";   // index 0 filled
    denseArray[1] = undefined; // index 1 filled
    denseArray[2] = "world";   // index 2 filled
    
    const arr2 = denseArray.map(word => word.toUpperCase()); //error
    
    console.log(arr2);

    As you can see, iterating a sparse array is fine, but if you have an explicit undefined, in the array, then word => word.toUpperCase() will fail because word is undefined.

    Sparse arrays are useful if you have numerically indexed data that you want to run .filter, .find, .map, .forEach and so on. Let's illustrate again:

    //some collection of records indexed by ID
    const people = [];
    people[17] = { id: 17, name: "Alice", job: "accountant" , hasPet: true  };
    people[67] = { id: 67, name: "Bob"  , job: "bank teller", hasPet: false };
    people[3] =  { id: 3 , name: "Carol", job: "clerk"      , hasPet: false };
    people[31] = { id: 31, name: "Dave" , job: "developer"  , hasPet: true  };
    
    
    /* some code that fetches records */
    const userChoice = 31;
    console.log(people[userChoice]);
    
    /* some code that transforms records */
    people
      .map(person => `Hi, I am ${person.name} and I am a ${person.job}.`)
      .forEach(introduction => console.log(introduction));
      
    /* different code that works with records */
    const petOwners = people
      .filter(person => person.hasPet)
      .map(person => person.name);
      
    console.log("Current pet owners:", petOwners)