JavaScript: Структури даних та алгоритми
JavaScript часто використовують для розробки фронтенд-додатків, але ця мова також поширена на бекенді завдяки Node.js. Незалежно від сфери застосування, знання структур даних і алгоритмів є важливою складовою для розробників, оскільки вони впливають на ефективність та підтримку коду. У цій статті ми розглянемо, які структури даних доступні в JavaScript, як їх застосовувати на практиці, а також обговоримо кілька базових алгоритмів, без яких не обходиться жоден серйозний проєкт.
Чому це важливо
- Продуктивність.
Правильний вибір структури даних (масив, список, хеш-таблиця) може радикально змінити швидкодію додатка. - Зручність коду.
Якщо структура відповідає задачі (наприклад, Set для унікальних елементів), код стає читабельнішим і легше підтримується. - Класичні алгоритми.
Сортування, пошук, робота з графами — усе це прямо чи опосередковано знадобиться для оптимізації.
Структури даних у JavaScript
Масив (Array)
Найпоширеніша структура даних у JS. Відрізняється від традиційних масивів у мовах на кшталт C++ чи Java, адже насправді це об’єкт із числовими індексами. Проте для більшості випадків можна вважати його динамічним масивом.
const arr = [10, 20, 30];
arr.push(40); // додає елемент у кінець
arr.pop(); // видаляє останній елемент
console.log(arr[1]); // 20
Порада: масиви підходять для більшості операцій, де потрібен швидкий доступ за індексом і швидке додавання в кінець. Однак вставка чи видалення елементу всередині вимагає O(n) часу.
Об’єкт (Object)
Об’єкти в JavaScript часто використовують як хеш-таблиці (key-value). Але вони мають обмеження, адже ключі завжди перетворюються на рядки. Також є ризик конфлікту з “вбудованими” ключами в prototype.
const obj = {};
obj['name'] = 'Alice';
obj.age = 25;
console.log(obj.name, obj.age);
Починаючи з ECMAScript 2015, з’явилися Map і Set, які точніше реалізують ідею “словника” та “множини”.
Map
Map — це структура ключ-значення, схожа на об’єкт, проте ключі можуть бути будь-якого типу (навіть об’єктами чи функціями).
const map = new Map();
map.set('name', 'Alice');
map.set({ id: 1 }, 'UserOne');
console.log(map.get('name')); // 'Alice'
console.log(map.size); // 2
Основні переваги:
- будь-який тип даних може бути ключем (не тільки рядок);
- упорядковані ітерації (в order вставки).
Set
Set — це структура, що зберігає унікальні значення. Додаючи елемент, якщо він уже присутній, операція ігнорується.
const set = new Set([10, 20, 30]);
set.add(20);
set.add(40);
console.log(set); // Set(4) { 10, 20, 30, 40 }
console.log(set.has(10)); // true
Зручно, коли потрібно тримати унікальні елементи (наприклад, збирання унікальних ID).
WeakMap і WeakSet
Це спеціалізовані структури, схожі на Map і Set, але ключі (в WeakMap) і значення (в WeakSet) є “слабкими” посиланнями на об’єкти. Це дає переваги в автоматичному очищенні (збирач сміття прибирає об’єкт, якщо більше немає сильних посилань). Корисно для кешування без ризику “витоку” пам’яті.
Алгоритми пошуку й сортування
Пошук
Лінійний пошук (Linear search) Перевіряємо кожен елемент у масиві, поки не знайдемо потрібний. Складність O(n). Для невеликих масивів достатньо, але якщо масив дуже великий — це повільно.
Бінарний пошук (Binary search) Працює тільки в відсортованому масиві. Починаємо з середини, якщо елемент більший від шуканого — йдемо в ліву частину, інакше — у праву. Складність O(log n).
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right)/2);
if (arr[mid] === target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
Сортування
JS має вбудований метод sort(), але важливо знати, що:
- За замовчуванням .sort() перетворює елементи на рядки і порівнює їх лексикографічно. Тобто [10, 2] перетворюється на [“10”, “2”], де “10” < “2” лексикографічно.
- Для числового сортування часто треба вказувати власну функцію порівняння.
const numbers = [10, 2, 30];
numbers.sort((a, b) => a - b); // [2, 10, 30]
При використанні .sort() всередині реалізовано алгоритм Timsort (O(n log n) у середньому). У більшості випадків цього достатньо.
Графи, дерева і колекції
Хоча вбудованих структур (Tree чи Graph) в JS немає, можна реалізувати самостійно:
- Дерево: створити клас Node з children і методами для обходу (DFS, BFS).
- Граф: використати Map для adjacency list (наприклад, ключ – вершина, значення – список сусідів).
Основні алгоритми:
- BFS (пошук у ширину). Використовується, щоб знайти найкоротший шлях у невагомому графі чи мінімальну кількість кроків.
- DFS (пошук у глибину). Зручно при обході дерево- або графоподібних структур.
- Dijkstra. Якщо потрібно шукати найкоротші шляхи у графі з вагами.
Приклад BFS у графі:
function bfs(start, graph) {
const visited = new Set();
const queue = [start];
visited.add(start);
while (queue.length) {
const current = queue.shift();
console.log(current);
for (const neighbor of graph.get(current) || []) {
if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push(neighbor);
}
}
}
}
У graph зберігається Map, де ключі — назви вершин, а значення — масив суміжних вершин.
Пам’ятайте, що в JS відсутнє вбудоване API для графів, доведеться робити все вручну.
Оптимізація із використанням структур
Як правильно підбирати структуру даних?
- Якщо потрібен швидкий доступ за індексом і часто додають елементи в кінець — Array.
- Якщо важлива унікальність — Set.
- Якщо потрібні ключ:значення зі швидким пошуком — Map.
- Якщо хочемо незмінні структури — застосовувати бібліотеки (immutable.js, наприклад) або нові пропозиції Records and Tuples (але вони ще не у фінальному стандарті).
Висновок
Структури даних (Array, Map, Set, Tree, Graph) і алгоритми (пошук, сортування, BFS/DFS) — це основа для ефективного коду. У JavaScript, незважаючи на те, що мова спочатку задумувалася як проста для браузерних задач, зараз є достатньо інструментів для реалізації як базових, так і складних структур. Важливо розуміти, як працюють масиви, об’єкти, Map та Set, щоб вибирати правильний інструмент для конкретної задачі. А вміння застосовувати алгоритми (лінійний або бінарний пошук, сортування, обходи у графах) допоможе створювати швидші й надійніші застосунки, які будуть добре масштабуватися разом із проєктом.