JavaScript arrays can be either dense or sparse. An array in JavaScript is a collection of multiple elements, and each element is assigned an index that we use to access it. The index number is a positive integer and starts from 0. As a result, if an array contains ten elements, its index number ranges from 0 to 9. But JavaScript also allows us to increase the array’s length without adding an element. Such an array is called a sparse array. In the tutorial, let us learn more about dense and sparse arrays.
An array is dense if it has values at every index. i. e., it has values at the index starting from 0 until the array.length - 1. The number of elements in the array is always equal to the array.length
For Example, the books array in the following example is dense. It has a length of 4 and contains values at every index starting from 0 to 3
const books = ["Ulysses", "Don Quixote", "War and Peace", "Moby Dick"];
console.log(books.length) //4
The length property in a dense array correctly returns the number of elements in the array.
The array is sparse when its length exceeds the number of elements. i.e., at least one of the indexes is empty, or the array has holes.
The following example creates a dense array of books with a length of 4. We set its length to 5. This will increase the array’s length to 5 but not store any value at the index 4. The books array will become sparse with no value at index 4.
//Example
const books = ["Ulysses", "Don Quixote", "War and Peace", "Moby Dick"];
//Set its length to 5
books.length = 5
//array length is 5
console.log(books.length) //5
The forEach method of the array does not iterate over the non-existent element of the array. That is the reason why it will print only four elements.
const books = ["Ulysses", "Don Quixote", "War and Peace", "Moby Dick"];
books.length = 5
//But it Prints only 4 element.
books.forEach(element => console.log(element));
//Ulysses
//Don Quixote
//War and Peace
//Moby Dick
The only good thing about sparse arrays is that they are memory efficient because they utilize less memory (No memory is used for empty items).
Looking up elements in a sparse array is slower because JavaScript treats them as objects and uses the object property lookup to retrieve values. The dense arrays use regular array lookup, which is faster than the property lookup.
Another important thing about a sparse array is that once it becomes sparse, it will never change back to dense, even if you assign values to all the empty values. So, make sure that you always use the array literal notation to create an array with all the values filled. You can assign undefined if you do not have a value.
Hence, unless you have any compelling reason to use a sparse array, always you dense array.
You can refer to the article Array Optimizations in JavaScript Engines for more information.
If we omit a value in Array Literal, it will result in a sparse array.
The values for indexes 3 and 4 are left out in the following example. This will produce a sparse array of only four items with a length of six.
//Example
const books = ["Ulysses", "Don Quixote", "War and Peace", , , "Moby Dick"];
console.log(books.length) //6
//But it Prints only 4 element.
books.forEach(element => console.log(element));
//Ulysses
//Don Quixote
//War and Peace
//Moby Dick
If we omit a value in Array Literal, it will result in a sparse array.
The values for indexes 3 and 4 are left out in the following example. This will produce a sparse array of only four items with a length of six.
//Example
const books = ["Ulysses", "Don Quixote", "War and Peace", , , "Moby Dick"];
console.log(books.length) //6
//But it Prints only 4 element.
books.forEach(element => console.log(element));
//Ulysses
//Don Quixote
//War and Peace
//Moby Dick
Invoking Array Constructor with only one numeric argument creates a sparse array.
In the following example, the Array(4) (or new Array(4)) creates a sparse array with length four and no elements.
const books = Array(4)
console.log(books.length) //4
books.forEach(element => console.log(element));
//no output
Deleting an item from the array using the delete operator does not re-arrange it but converts it into a sparse array.
const nums = Array("1","2","3","4")
console.log(nums.length) //4
delete nums[2]
//only 3 elements in the array
nums.forEach(element => console.log(element));
//1
//2
//4
//But length still shows 4
console.log(nums.length) //4
We can set the length property of an array to a value that is higher than the highest index
const books = ["Ulysses", "Don Quixote" ];
books.length = 4
//But it Prints only 2 element.
books.forEach(element => console.log(element));
//Ulysses
//Don Quixote
//Length returns returns the length as 4
console.log(books.length) // 4
Inserting a value at a non-existing index greater than the length will turn the array into a sparse array.
const books = ["Ulysses", "Don Quixote" ];
books[10]="Test" //Insert value at random index
console.log(books.length) //11
JavaScript returns undefined when we try to read the empty holes from a sparse array.
The code below deletes the element at index one from the nums1 array. When we try to access nums1[1], we get undefined. While looping through the array, it will skip index one as there is no element.
var nums1 = [0, 1, 2, 3];
delete nums1[1]
nums1.forEach(function(item) {
console.log(item); // 0,2,3 //Only 3 elements. Index 1 is deleted
});
console.log(nums1[1]); //undefined
The following code sets the value of nums2[1] to undefined. This does not convert nums2 to the sparse array because index one does have a value, and it is undefined.
var nums2 = [0, 1, 2, 3];
nums2[1]=undefined
nums2.forEach(function(item) {
console.log(item); // 0,undefined,2, 3 //element at index 1 has value undefined
});
console.log(nums2[1]); //undefined
The holes are falsy in JavaScript.
var nums = [0, 1, 2, 3, undefined];
delete nums[1]
if (nums[1]) {
console.log('Holes are Truthy') //Not Printed
}
if (!nums[1]) {
console.log('Holes are Falsy')
}
The easiest way to check if the array is Sparse is to use the in operator to test if the index exists in an array. The syntax of the in operator is shown below.
index in array
It will return true if the element exists at the index else return false.
var arr1 = ["Hello",,"World"]; // This is sparse array with no element at index 1
var arr2 = ["Hello",undefined,"World"]; // This array is not sparse. It has a element at index 1 with value undefined
console.log(1 in arr1) // => false: arr1 has no element at index 1
console.log(1 in arr2) // => true: arr2 has a element at index 1
The following isSparse function accepts an array as an argument and loops through every index using a for loop.
function isSparse(array) {
for (let i = 0; i < array.length; i++) {
if (!(i in array)) {
return true;
}
}
return false;
}
var nums1 = [0, 1, 2, 3];
console.log(isSparse(nums1)) //false
delete nums1[1]
console.log(isSparse(nums1)) //true
Note that if you fill-up the holes of a sparse array, it will not become dense but remain sparse internally. There is no easy way to detect such an array.
The following code uses the for loop to traverse the sparse array and convert it to the dense array.
sparseArray = [, undefined, 1, 2, , 3, ,];
denseArray = [];
for(var i = 0; i < sparseArray.length; i++)
if(i in sparseArray)
denseArray.push(sparseArray[i]);
We can also use the Object.values() method to convert a sparse array to a dense array. It creates a new array from the object’s own enumerable properties and returns it.
const sparseArray = [, , 1, 2, , 3, ,];
const denseArray = Object.values(sparseArray);
console.log(denseArray);
The above methods only remove the holes from the array. They will not remove elements explicitly set to undefined or null.
The array methods in JavaScript treat sparse arrays inconsistently. Some treat them as undefined (usually the newer methods) while others skip them (older methods).
The following methods do not treat them as undefined. How exactly they treat them depends on the method.
concat()
copyWithin()
every()
filter()
flat()
flatMap()
forEach()
indexOf()
lastIndexOf()
map()
reduce()
reduceRight()
reverse()
slice()
some()
sort()
splice()
These methods treat empty slots as if they are undefined
entries()
fill()
find()
findIndex()
findLast()
findLastIndex()
group()
groupToMap()
includes()
join()
keys()
toLocaleString()
values()