I decided to write about sparse and dense arrays several months ago. I thought it would be easy and imagined writing a masterpiece based off my multi-year experience with arrays.
Alas, my foray into the intricacies of Arrays unearthed surprising discoveries and shattered my brittle expertise. This series of posts describes my learnings and Aha moments.
Myriad Ways of Making Arrays
Nearly everyone uses [] to create arrays. There are 4 other ways to do make arrays.
1. Making Empty Arrays
let arr1 = []; // array literal
let arr2 = new Array();
let arr3 = Array(); // without the new keyword
Invoking the Array Constructor without the new prefix is not recommended.
Tweet
2. Making Empty Arrays with a Specific Length
// Creating an empty array of length 2
let arr3 = [,,]; // Last comma is ignored
let arr4 = new Array(2); // has 2 holes!
3. Array with Elements
// Creating an array with elements
let arr5 = [1, 2, 3];
let arr6 = new Array(1,2,3);
4. Using the ES2015 Array.Of Static Method
// Creating an array with elements
let a = Array.of(1,2,3); // [1,2,3]
let b = Array.of(1); // [1]
Note that Array.of(7) creates an array with a single element 7 while new Array(7) creates an Array with 7 empty slots.
The Confusing Array Constructor
The variadic nature of the Array Constructor is confusing. If invoked with a single argument, that value is interpreted as the length of the array to create. However, there are more arguments, they get interpreted as elements of the array!
The Array Constructor explained
- If invoked with a single argument:
- If the argument is a number, then the value must be a Natural Number less than 232-1; i.e. the integer interval [0..232-1]. Otherwise, it throws an error.
- If the argument is non-numeric (e.g. null), it becomes the only element in the array.
- If invoked with multiple arguments; these values become the array elements.
Let’s look at the code!
// Rule 1.1
new Array(2); // [empty x 2]
new Array(-1); // error
// Rule 1.2
new Array('2'); // ['2']
// Rule 2
new Array(-100, '1'); // [-100, '1']
How would you create an array with just one number using the Array constructor?
How big can Arrays get?
The maximum length of an array in JavaScript is 4294967295 (232 – 1). Let’s see what happens if we exceed that value.
let maxLen = 2**32 - 1;
new Array(maxLen + 1);
// Uncaught RangeError: Invalid array length
let bigArray = new Array(maxLen);
bigArray.length = maxLen + 1;
// Uncaught RangeError: Invalid array length
Let’s see a way to bypass this limit by leveraging our knowledge that arrays are objects under the covers.
let maxLen = 2**32 - 1;
let bigArray = new Array(maxLen);
bigArray[maxLen+1] = 1;
console.log(bigArray[maxLen+1]); // 1
console.log(maxLen);
// (4294967295) [empty × 4294967295, 4294967296:1]
bigArray.push(2);
// RangeError:Invalid array length
console.log(maxLen);
// (4294967295)
// [empty × 4294967295, 4294967296:1, 4294967295:2]
Two eye-opening discoveries:
- push throws an error but still sets the property value.
- Running console.table(bigArray) crashes FireFox. Try it.
The Array Length property
JavaScript array indices start from zero; thus the length is always 1 more than the last array element index.
let arr = [];
console.log(arr.length); // 0
arr[4] = 4;
console.log(arr.length); // 5
Length is Settable
The following code snippet increases the array size to 5.
let arr = [];
arr.length = 5;
console.log(arr);
// (4) [empty x 5]
This trick can be used to trim arrays by reducing length.
let arr = [1,2,3,4,5,6,7,8,9,10];
arr.length = 3;
console.log(arr);
// [1,2,3]
Emptying Arrays
You can set length to 0 or reassign the reference to a new array.
let arr = [1,2,3];
arr.length = 0;
arr = []; // faster
The similitude is being asked to empty a sack filled with clothes.
- You can take out each individual cloth piece one at a time (set length to zero).
- You can replace the sack with a brand new empty sack (re-assigning to []). The old sack and its contents still exist somewhere (see closures).
Just like in the real life example, it is faster to do assign an empty array. Setting the length to zero clears the array by deleting each array element and is thus understandably slower.
Clearing all references
let a = Array.of(1,2,3);
let b = a;
b.length = 0;
console.log(a); // []
This approach can be a double-edged sword since it clears all references but it comes handy for scenarios when arrays are declared with const.
Clearing only the reference
let a = Array.of(1,2,3);
let b = a;
b = []
console.log(a); // [1,2,3]
Do you know arrays are objects under the covers? The next post dives into these subtleties and promises more fun.
References
- http://speakingjs.com/es5/ch18.html
- https://stackoverflow.com/questions/1510778/are-javascript-arrays-sparse
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections
- https://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of