Робота з масивами в JavaScript рідко потребує написання циклу for. Тепер мова надає виразні інструменти, які перетворюють вкладені, захаращені дані на чисті, зрозумілі трансформації. Цей посібник продовжує те, на чому зупиняються основи — зосереджуючись на розгортанні (flattening), редукції (reducing), запитах та новіших патернах незмінності (immutable patterns), які дозволяють не змінювати ваші вихідні дані.
Розгортання вкладених структур за допомогою flat() та flatMap()
Реальні дані люблять вкладеність. Отримуйте записи з API, парсіть CSV або читайте конфігураційний файл — і ви часто будете отримувати масиви всередині масивів. flat() існує для того, щоб стиснути ці рівні в один лінійний список.
Викличте flat() на вкладеному масиві, і за замовчуванням він розгорне лише один рівень глибини:
const nested = [[1, 2], [3, 4]];
const flat = nested.flat();
// [1, 2, 3, 4]
Початковий масив nested залишається точно таким, яким він був. Якщо вкладеність глибша, передайте аргумент глибини, наприклад flat(2) або навіть flat(Infinity), якщо ви не знаєте, скільки рівнів існує. Це краще, ніж писати рекурсивні допоміжні функції вручну.
Коли вам потрібно трансформувати елементи і розгорнути результат, flatMap() робить і те, і інше за один прохід. Він виконує функцію відображення (mapping) для кожного елемента, а потім розгортає отримані масиви на один рівень. Використання окремих викликів .map().flat() працює, але flatMap() пропускає проміжний масив, що економить пам'ять і позбавляє від другої ітерації. Класичний приклад використання — розбиття масиву речень на окремі слова:
const lines = ['hello world', 'foo bar'];
const words = lines.flatMap(line => line.split(' '));
// ['hello', 'world', 'foo', 'bar']
Фільтрація та відображення: знайте різницю
filter() створює новий масив, що містить лише ті елементи, які пройшли вашу перевірку. Функція зворотного виклику (callback) має повертати "truthy" значення, щоб залишити елемент, або "falsy" значення, щоб видалити його. Він ніколи не змінює вихідний масив.
const users = [
{ name: 'Ajay', active: true },
{ name: 'Bina', active: false }
];
const activeUsers = users.filter(u => u.active);
filter() легко переплутати з map(), оскільки обидва повертають нові масиви та приймають callback-функції. Різниця полягає в "контракті". map() повертає новий масив тієї ж довжини. Трансформуйте кожен елемент, виконайте умову, і ви дійсно побачите true або false для кожного окремого слота, тому що map() ніколи не видаляє елементи — він лише змінює їх. filter(), з іншого боку, повертає потенційно коротший масив, що містить тільки ті елементи, які пройшли вашу перевірку. Якщо ви хочете видалити дані, використовуйте filter(). Якщо ви хочете трансформувати кожен фрагмент даних, зберігаючи їхню кількість, використовуйте map().
Стиснення масивів за допомогою reduce()
У той час як map() та filter() повертають масиви, reduce() зводить увесь список до одного значення. Він проходить зліва направо, проносячи акумулятор крізь кожен крок.
const totals = [10, 20, 30];
const sum = totals.reduce((acc, current) => acc + current, 0);
// 60
Другий аргумент — 0 у наведеному вище прикладі — це початкове значення акумулятора. Пропустіть його, і JavaScript використає перший елемент як відправну точку, починаючи ітерацію з індексу 1. Це часто стає причиною помилок, коли масив порожній, оскільки виклик reduce() без початкового значення на порожньому масиві викликає TypeError.
reduce призначений не лише для арифметики. Ви можете використовувати його для побудови таблиць частотності, об'єднання об'єктів, пошуку максимальних значень або передачі даних через кілька трансформацій. Оскільки він такий гнучкий, варто тримати callback-функцію невеликою та чітко називати змінні. Щільний блок reduce() швидко стає важчим для читання, ніж цикл, який він замінив.
Булеві запити: every() та some()
Іноді вам не потрібен новий масив; вам потрібна відповідь "так" чи "ні" щодо колекції. every() повертає true лише тоді, коли всі елементи задовольняють вашу умову. Якщо найперший елемент не проходить перевірку, метод негайно зупиняється і повертає false.
const scores = [85, 92, 78];
const allPassed = scores.every(s => s >= 50);
some() є дзеркальним відображенням. Він повертає true, якщо хоча б один елемент проходить перевірку. Знайшовши збіг, він миттєво припиняє виконання (short-circuits).
const hasPerfectScore = scores.some(s => s === 100);
Ці методи замінюють ручні цикли для перевірок валідації. Потрібно підтвердити, що всі поля форми заповнені? Використовуйте every(). Потрібно дізнатися, чи має хоча б один користувач права адміністратора? Використовуйте some().
Перегляд та оновлення без мутації
Сучасний JavaScript сильно схиляється до незмінності (immutability). Ці методи допомагають переглядати або оновлювати масиви, не чіпаючи оригінал.
keys() повертає об'єкт-ітератор, що містить індекси масиву. entries() повертає ітератор пар [index, value]. Вони чудово проявляють себе в циклах for...of, коли ви хочете використовувати чисте деструктурування:
const fruits = ['apple', 'banana'];
for (const [index, fruit] of fruits.entries()) {
console.log(index, fruit);
}
with() — це нове доповнення до мови, яке замінює елемент за певним індексом і повертає абсолютно новий масив. Воно вирішує ту саму задачу, що й присвоєння через квадратні дужки — arr[index] = value, — проте залишає вихідний масив незмінним. У таких фреймворках, як React, де пряма мутація порушує механізм відстеження змін, with() є безпечнішим шляхом:
const original = [10, 20, 30];
const updated = original.with(1, 99);
// original: [10, 20, 30]
// updated: [10, 99, 30]
Spread та Rest: два напрямки ...
Синтаксис spread та rest-параметр мають ті самі три крапки, але вони переміщують дані у протилежних напрямках.
Використовуйте оператор spread, щоб розгорнути масив на окремі елементи. Це найчистіший спосіб об'єднати масиви або клонувати їх:
const front = [1, 2];
const back = [3, 4];
const merged = [...front, ...back];
const copy = [...merged];
Ви також можете легко додати додаткові елементи: [0, ...front, 99, ...back].
Rest-параметр збирає кілька елементів в один масив. У сигнатурі функції він збирає всі аргументи, що залишилися:
function logFirst(first, ...rest) {
console.log('First:', first);
console.log('Rest:', rest);
}
logFirst('a', 'b', 'c');
// First: a
// Rest: ['b', 'c']
Разом вони допомагають виконувати поширене завдання: приймати варіативний ввід, обробляти його як масив, а потім знову розгортати у виклики функцій або нові структури даних.
Головний висновок
Мета цих методів — не вразити колег однорядковими конструкціями, а зробити ваші наміри очевидними. Коли інший розробник бачить filter(), він розуміє, що ви видаляєте елементи. Коли він бачить reduce(), він розуміє, що ви згортаєте дані. Коли він бачить with(), він знає, що оригінальний масив десь залишився цілим. Володіння цими інструментами означає написання коду, який пояснює сам себе, і це заощаджує набагато більше часу, ніж будь-який хитромудрий цикл.
