Skip to main content

Dense and sparse arrays

A dense array is an array in which every index from 0 to length - 1 is occupied, while a sparse array is one where some indexes are missing, that is, it has holes. The difference is not cosmetic: it decides which elements the iteration methods can see and how fast the engine works with the array.

Theory

TL;DR

  • Dense array: every index from 0 to length - 1 really exists.
  • Sparse array: some indexes are missing, and length is larger than the number of real elements.
  • A hole is not the value undefined: the property with that key simply does not exist, so i in arr gives false.
  • Holes appear in three ways: delete, a skipped item in the literal [1, , 3], and manually increasing length.
  • forEach, map, filter, reduce and Object.keys skip holes, while for, for...of, spread and Array.from see them as undefined.
  • Sparse arrays are slower, so instead of holes it is better to store an explicit null or undefined.

Quick example

javascript
const dense = [1, 2, 3]; const sparse = [1, , 3]; dense.forEach(v => console.log(v)); // 1, 2, 3 sparse.forEach(v => console.log(v)); // 1, 3 (index 1 is skipped) console.log(Object.keys(dense)); // ['0', '1', '2'] console.log(Object.keys(sparse)); // ['0', '2']

Both arrays have length === 3, but the second one has only two real elements.

Definitions

A dense array is an array in which all indexes are occupied by elements, from 0 to length - 1.

That is:

  • there are no skipped indexes (no holes),
  • every index really exists in the object.
javascript
const dense = [10, 20, 30];

Here:

  • the indexes are 0, 1, 2
  • dense.length === 3
  • 0 in dense is true, 1 in dense is true, 2 in dense is true

A sparse array is an array in which some indexes are missing, so it contains holes.

javascript
const sparse = [10, , 30]; console.log(sparse); // [10, <1 empty item>, 30]

Here:

  • index 1 does not exist (1 in sparse is false)
  • sparse.length === 3, but there are really only two elements

How holes appear

  1. By using delete:

    javascript
    const arr = [1, 2, 3]; delete arr[1]; console.log(arr); // [1, <1 empty item>, 3]
  2. By skipping an item in a literal:

    javascript
    const arr = [1, , 3];
  3. By increasing length manually:

    javascript
    const arr = [1, 2]; arr.length = 5; console.log(arr); // [1, 2, <3 empty items>]
  4. By writing to a far index:

    javascript
    const arr = [1]; arr[4] = 5; console.log(arr); // [1, <3 empty items>, 5]

Worth knowing separately: new Array(3) also creates a sparse array of three holes, while Array.from({ length: 3 }) gives a dense array of three undefined values.

Behaviour during iteration

MethodDense arraySparse array
forwalks every indexwalks every index, a hole reads as undefined
for...ofwalks every valuewalks every position, a hole reads as undefined
forEach()visits every elementskips the empty ones
map()processes every elementskips the empty ones but keeps the holes in the result
filter(), reduce(), some(), every()process every elementskip the empty ones
inchecks that the index existsreturns false for holes
Object.keys()lists every indexleaves the empty ones out
spread [...arr], Array.from()copies the valuesturns holes into undefined
join(), toString()the usual resulta hole becomes an empty string

The main trap is that the very same hole "does not exist" for some methods and becomes undefined for others.

javascript
const sparse = [1, , 3]; console.log(sparse.map(v => v * 2)); // [2, <1 empty item>, 6] console.log([...sparse]); // [1, undefined, 3] console.log(sparse.join('-')); // '1--3'

Impact on performance

Sparse arrays:

  • run slower,
  • are optimised worse by the V8 engine, because it moves the array from the fast elements representation into a dictionary one,
  • and often behave oddly during iteration.

That is why in real projects it is better to avoid arrays with holes and use null or undefined as explicit "empty" values.

If a sparse array has already appeared, it is easy to make it dense again:

javascript
const sparse = [1, , 3]; const filled = Array.from(sparse); // [1, undefined, 3] const cleaned = sparse.filter(() => true); // [1, 3], the holes are gone

In short

Array typeDescriptionExampleBehaviour
DenseAll indexes are occupied[1, 2, 3]Predictable, fast
SparseSome indexes are missing[1, , 3]Methods skip the empty ones, slower

Common mistakes

  • Thinking that a hole is undefined. There is no value at all; check with i in arr or Object.hasOwn(arr, i) rather than comparing against undefined.
  • Trusting length as the element count. In a sparse array length is bigger than the real number of elements; count with Object.keys(arr).length.
  • Creating an array with new Array(n) and then iterating it. new Array(3).map(...) does nothing, because every position is a hole; use Array.from({ length: 3 }, (_, i) => i).
  • Expecting all methods to behave the same. map() skips holes, while spread and for...of turn them into undefined.
  • Using delete to remove elements. That is the most common way sparse arrays appear in production code; use splice() instead.
  • Increasing length manually to "reserve space". This does not speed anything up, it just makes the array sparse.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.