Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Щільні та розріджені масиви». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Щільний (dense) масив, це масив, у якому зайняті всі індекси від `0` до `length - 1`, а розріджений (sparse), це масив, у якому частина індексів фізично відсутня, тобто в ньому є «дірки».** Формально дірка означає, що властивість з таким ключем не існує: `1 in sparse` поверне `false`, хоча `sparse[1]` дасть `undefined`. Дірки з'являються після `delete`, від пропуску в літералі (`[1, , 3]`) або після ручного збільшення `length`. Методи `forEach`, `map`, `filter`, `reduce` та `Object.keys` порожні слоти пропускають, а двигуни оптимізують такі масиви гірше, тому в реальному коді краще використовувати явні `null` чи `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'] ``` **Ключове:** у щільному масиві існують усі індекси, у розрідженому частина індексів відсутня, і саме через це методи перебору поводяться по-різному.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Щільний (dense) масив, це масив, у якому зайняті всі індекси від `0` до `length - 1`, а розріджений (sparse), це масив, у якому деякі індекси відсутні, тобто в ньому є «дірки».** Різниця не косметична: від неї залежить, які елементи побачать методи перебору і наскільки швидко двигун працюватиме з масивом. ## Теорія ### TL;DR - **Щільний масив:** усі індекси від `0` до `length - 1` реально існують. - **Розріджений масив:** частина індексів відсутня, `length` більший за кількість справжніх елементів. - Дірка, це не значення `undefined`: властивості з таким ключем просто немає, тому `i in arr` дає `false`. - Дірки з'являються трьома шляхами: `delete`, пропуск у літералі `[1, , 3]`, ручне збільшення `length`. - `forEach`, `map`, `filter`, `reduce` та `Object.keys` дірки пропускають, а `for`, `for...of`, spread і `Array.from` бачать їх як `undefined`. - Розріджені масиви повільніші, тому замість дірок краще зберігати явні `null` або `undefined`. ### Швидкий приклад ```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 (індекс 1 пропущено) console.log(Object.keys(dense)); // ['0', '1', '2'] console.log(Object.keys(sparse)); // ['0', '2'] ``` Обидва масиви мають `length === 3`, але справжніх елементів у другому лише два. ### Визначення **Щільний масив (dense array)**, це масив, у якому **всі індекси зайняті елементами**, від `0` до `length - 1`. Тобто: - немає «пропущених» індексів (дірок), - кожен індекс справді існує в об'єкті. ```javascript const dense = [10, 20, 30]; ``` Тут: - індекси: `0, 1, 2` - `dense.length === 3` - `0 in dense` дає `true`, `1 in dense` дає `true`, `2 in dense` дає `true` **Розріджений масив (sparse array)**, це масив, у якому **деякі індекси відсутні**, у ньому є «дірки». ```javascript const sparse = [10, , 30]; console.log(sparse); // [10, <1 empty item>, 30] ``` Тут: - індекс `1` **не існує** (`1 in sparse` дає `false`) - `sparse.length === 3`, але елементів реально лише два ### Як з'являються «дірки» 1. Через **використання** `delete`: ```javascript const arr = [1, 2, 3]; delete arr[1]; console.log(arr); // [1, <1 empty item>, 3] ``` 2. Через **пропуск у літералі**: ```javascript const arr = [1, , 3]; ``` 3. Через **ручне збільшення** `length`: ```javascript const arr = [1, 2]; arr.length = 5; console.log(arr); // [1, 2, <3 empty items>] ``` 4. Через запис у далекий індекс: ```javascript const arr = [1]; arr[4] = 5; console.log(arr); // [1, <3 empty items>, 5] ``` Окремо варто знати: `new Array(3)` теж створює розріджений масив з трьох дірок, а `Array.from({ length: 3 })` дає щільний масив із трьох `undefined`. ### Поведінка під час перебору | Метод | Щільний масив | Розріджений масив | | --- | --- | --- | | `for` | проходить по всіх індексах | проходить по всіх індексах, дірка дає `undefined` | | `for...of` | проходить по всіх значеннях | проходить по всіх позиціях, дірка дає `undefined` | | `forEach()` | проходить по кожному елементу | пропускає «порожні» | | `map()` | обробляє кожен елемент | пропускає «порожні», але зберігає дірки в результаті | | `filter()`, `reduce()`, `some()`, `every()` | обробляють кожен елемент | пропускають «порожні» | | `in` | перевіряє наявність індексу | повертає `false` для дірок | | `Object.keys()` | показує всі індекси | пропускає порожні | | spread `[...arr]`, `Array.from()` | копіює значення | перетворює дірки на `undefined` | | `join()`, `toString()` | звичайний результат | дірка стає порожнім рядком | Головна пастка тут у тому, що одна й та сама дірка в одних методах «не існує», а в інших перетворюється на `undefined`. ```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' ``` ### Вплив на продуктивність Розріджені масиви: - **працюють повільніше**, - **гірше оптимізуються двигуном V8**, бо він переводить масив з швидкого «елементного» представлення у словникове, - і часто поводяться «дивно» під час ітерації. > Тому в реальних проєктах краще **уникати «дірявих» масивів** і використовувати `null` або `undefined` як явні значення «порожнечі». Якщо розріджений масив уже утворився, його легко ущільнити: ```javascript const sparse = [1, , 3]; const filled = Array.from(sparse); // [1, undefined, 3] const cleaned = sparse.filter(() => true); // [1, 3], дірки прибрано ``` ### Коротко | Тип масиву | Опис | Приклад | Поведінка | | --- | --- | --- | --- | | **Щільний** | Усі індекси зайняті | `[1, 2, 3]` | Передбачувана, швидка | | **Розріджений** | Є пропущені індекси | `[1, , 3]` | Методи пропускають порожні, повільніше | ### Типові помилки - **Вважати, що дірка, це `undefined`.** Значення немає взагалі; перевіряйте через `i in arr` або `Object.hasOwn(arr, i)`, а не через порівняння з `undefined`. - **Довіряти `length` як кількості елементів.** У розрідженому масиві `length` більший за реальну кількість елементів; рахуйте через `Object.keys(arr).length`. - **Створювати масив через `new Array(n)` і перебирати його.** `new Array(3).map(...)` нічого не зробить, бо всі позиції, це дірки; беріть `Array.from({ length: 3 }, (_, i) => i)`. - **Очікувати однакової поведінки від усіх методів.** `map()` дірки пропускає, а spread і `for...of` перетворюють їх на `undefined`. - **Використовувати `delete` для видалення елементів.** Саме так у продакшн-коді найчастіше й з'являються розріджені масиви; беріть `splice()`. - **Збільшувати `length` вручну, щоб «зарезервувати місце».** Це не прискорює код, а навпаки, робить масив розрідженим.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.