Suggest an editImprove this articleRefine the answer for “Dense and sparse arrays”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**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 physically missing, that is, it has holes.** Formally a hole means the property with that key does not exist: `1 in sparse` returns `false`, even though `sparse[1]` gives `undefined`. Holes appear after `delete`, from a skipped item in a literal (`[1, , 3]`), or after manually increasing `length`. The methods `forEach`, `map`, `filter`, `reduce` and `Object.keys` skip empty slots, and engines optimise such arrays worse, so in real code it is better to store an explicit `null` or `undefined`. ```javascript const dense = [1, 2, 3]; const sparse = [1, , 3]; console.log(Object.keys(dense)); // ['0', '1', '2'] console.log(Object.keys(sparse)); // ['0', '2'] ``` **Key point:** a dense array has every index present, a sparse array is missing some of them, and that is exactly why iteration methods behave differently.Shown above the full answer for quick recall.Answer (EN)Image**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 | Method | Dense array | Sparse array | | --- | --- | --- | | `for` | walks every index | walks every index, a hole reads as `undefined` | | `for...of` | walks every value | walks every position, a hole reads as `undefined` | | `forEach()` | visits every element | skips the empty ones | | `map()` | processes every element | skips the empty ones but keeps the holes in the result | | `filter()`, `reduce()`, `some()`, `every()` | process every element | skip the empty ones | | `in` | checks that the index exists | returns `false` for holes | | `Object.keys()` | lists every index | leaves the empty ones out | | spread `[...arr]`, `Array.from()` | copies the values | turns holes into `undefined` | | `join()`, `toString()` | the usual result | a 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 type | Description | Example | Behaviour | | --- | --- | --- | --- | | **Dense** | All indexes are occupied | `[1, 2, 3]` | Predictable, fast | | **Sparse** | Some 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.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.