Обхід двовимірного масиву
Обхід двовимірного масиву (матриці) це одна з базових, але дуже важливих операцій. Оскільки це масив масивів, його обходять вкладеними циклами: зовнішній іде по рядках, внутрішній по стовпцях. Спосіб обходу обирають за тим, чи потрібні індекси і чи треба мати змогу перервати цикл.
Теорія
TL;DR
- Двовимірний масив обходять вкладеними циклами: зовнішній по рядках, внутрішній по елементах рядка.
- Класичний
forдає повний контроль і доступ до індексівiтаj. - Два вкладені
for...ofце найчитабельніший сучасний варіант. forEach()дає індекси другим аргументом, але його не можна перервати черезbreak.flat()перетворює матрицю на одновимірний масив, коли індекси не потрібні.reduce()підходить для агрегації, наприклад суми всіх елементів.
Швидкий приклад
Усі приклади нижче працюють із цією матрицею:
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
for (const row of matrix) {
for (const value of row) {
console.log(value); // 1, 2, 3, 4, ... 9
}
}Класичний спосіб через for
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(`matrix[${i}][${j}] = ${matrix[i][j]}`);
}
}iце індекс рядка,jце індекс стовпця,matrix[i][j]це елемент у рядкуiта стовпціj.
Вивід:
matrix[0][0] = 1
matrix[0][1] = 2
// ...
matrix[2][2] = 9Цей варіант дає повний контроль: можна йти у зворотному напрямку, пропускати елементи, вийти через break або continue.
for...of і forEach()
for...of проходить по значеннях, тому код коротший:
for (const row of matrix) {
for (const value of row) {
console.log(value);
}
}Зовнішній for...of іде по рядках, внутрішній по елементах усередині рядка. Це найчитабельніший і найсучасніший спосіб обходу, і його все ще можна перервати через break.
forEach() робить те саме у функціональному стилі й одразу дає індекси:
matrix.forEach((row, i) => {
row.forEach((value, j) => {
console.log(`matrix[${i}][${j}] = ${value}`);
});
});Він не потребує ручного лічильника і добре лягає у функціональний стиль, але перервати його через break або return не вийде: return лише завершує поточний виклик колбека.
flat(), коли індекси не потрібні
Якщо треба пройтися по всіх елементах поспіль, матрицю можна «розгорнути»:
matrix.flat().forEach(value => console.log(value));flat() робить із двовимірного масиву звичайний одновимірний:
matrix.flat(); // [1, 2, 3, 4, 5, 6, 7, 8, 9]Мінус у тому, що після цього ви втрачаєте інформацію про рядок і стовпець, а також створюєте новий масив у пам'яті.
reduce() для підрахунків
Якщо потрібно, наприклад, підсумувати всі елементи:
const sum = matrix.reduce(
(acc, row) => acc + row.reduce((a, b) => a + b, 0),
0
);
console.log(sum); // 45Внутрішній reduce підсумовує рядок, зовнішній збирає все разом.
Порівняння способів
| Спосіб | Що робить | Чи можна перервати цикл |
|---|---|---|
for | Повний контроль, індекси | Так |
for...of | Простий і читабельний обхід | Так |
forEach() | Функціональний стиль | Ні |
flat() плюс forEach() | Обхід усіх значень як одновимірного масиву | Ні |
reduce() | Агрегація, наприклад сума | Так, логічно |
Підсумок: для обходу двовимірного масиву
javascriptfor (const row of matrix) { for (const value of row) { console.log(value); } }це сучасний, читабельний і безпечний спосіб.
Типові помилки
breakусерединіforEach(). Це синтаксична помилка, аreturnвиходить лише з одного виклику колбека. Якщо потрібен ранній вихід, берітьforабоfor...of.- Один цикл замість двох.
matrix.forEach(row => console.log(row))виведе масиви, а не значення; для значень потрібен другий рівень обходу. - Межа за
matrix[0].length. На «рваних» масивах із рядками різної довжини це або пропустить елементи, або дастьundefined; берітьmatrix[i].length. for...inзамістьfor...of.for...inперебирає ключі, тобто рядкові індекси"0","1", і ще й підхоплює власні перелічувані властивості масиву.flat()там, де потрібні координати. Після вирівнювання неможливо сказати, з якого рядка прийшло значення.- Зміна довжини масиву під час обходу.
pushабоspliceусередині циклу зсуває індекси і призводить до пропущених чи повторених елементів.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.