JavaScript
Loop Arrays & Objects
Nested loop
เรียนรู้การใช้ nested `for` loop เพื่อวนข้อมูล 2 ชั้น สร้างข้อความหลายบรรทัด และเขียน function ที่ `return` multiline string ออกมาได้
Nested loop คืออะไร — loop ซ้อน loop เพื่อจัดการข้อมูล 2 ชั้น
Nested loop คือการเอา loop หนึ่งไปวางไว้ข้างในอีก loop หนึ่ง วิธีคิดที่ง่ายที่สุดคือ: • loop นอก คุมรอบหลัก หรือคุมแต่ละแถว • loop ใน คุมสมาชิกย่อยในรอบนั้น แนวคิดนี้ใช้บ่อยมากกับข้อมูลแบบ grid, matrix, nested array และงานสร้างข้อความทีละบรรทัด
| ส่วน | หน้าที่ | ตัวอย่างที่พบบ่อย |
|---|---|---|
| loop นอก | คุมจำนวนแถวหรือจำนวนบรรทัด | แถวในตาราง, บรรทัดของ pattern |
| loop ใน | คุมสมาชิกย่อยในแต่ละรอบ | คอลัมน์ในแถว, จำนวน `*` ในบรรทัดนั้น |
ในแต่ละรอบของ loop นอก loop ในจะรันครบทุกครั้งก่อน แล้วค่อยกลับไปขึ้นรอบถัดไปของ loop นอก
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 4; col++) {
console.log("row =", row, "col =", col);
}
}ใช้ nested loop กับ nested array — วนทีละแถว แล้ววนค่าข้างในแถวนั้น
ถ้าเรามี nested array 2 มิติ เรามักใช้ loop นอกวนตามแถว และใช้ loop ในวนค่าภายในแถวนั้น รูปแบบมาตรฐานคือ `grid[row][col]` โดย `row` มาจาก loop นอก และ `col` มาจาก loop ใน
สังเกตว่าเรา reset `rowText` ใหม่ในแต่ละรอบของ loop นอก เพื่อให้ข้อความของแต่ละแถวไม่ไหลไปรวมกัน
const grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
for (let row = 0; row < grid.length; row++) {
let rowText = "";
for (let col = 0; col < grid[row].length; col++) {
rowText += grid[row][col] + " ";
}
console.log("row " + row + ": " + rowText.trim());
}
// row 0: 1 2 3
// row 1: 4 5 6
// row 2: 7 8 9nested loop ใช้กับ accumulator pattern ได้เหมือน loop ปกติ เพียงแค่ตอนนี้เราวนข้อมูล 2 ชั้น
const board = [
["X", "O", "X"],
["O", "X", "O"],
["X", "X", "O"],
];
let xCount = 0;
for (let row = 0; row < board.length; row++) {
for (let col = 0; col < board[row].length; col++) {
if (board[row][col] === "X") {
xCount++;
}
}
}
console.log(xCount); // 5ใช้ nested loop เพื่อสร้างข้อความหลายบรรทัด
อีก use-case ที่พบบ่อยคือการสร้าง pattern string เช่น ดาวหลายบรรทัด รูปแบบหลักมี 4 ขั้น: 1. loop นอกคุมจำนวนบรรทัด 2. ในแต่ละบรรทัด สร้าง `line = ""` 3. loop ในต่อ `"*"` ทีละตัวลงใน `line` 4. เอา `line` ไปต่อใน `result` และใช้ `if` กัน `\n` หลังบรรทัดสุดท้าย
จำนวนรอบของ loop ในเปลี่ยนตาม `row` จึงได้จำนวนดาวเพิ่มขึ้นทีละบรรทัด
const rows = 4;
let result = "";
for (let row = 1; row <= rows; row++) {
let line = "";
for (let col = 0; col < row; col++) {
line += "*";
}
result += line;
if (row < rows) {
result += "\n";
}
}
console.log(result);
// *
// **
// ***
// ****เขียนเป็น function แล้ว `return` string ออกมา
ถ้าอยากเอา pattern นี้ไปใช้ซ้ำหลายครั้ง ควรห่อไว้ใน function แล้วใช้ `return` ส่งผลลัพธ์ออกมา จุดสำคัญคือ function นี้ไม่ได้มีหน้าที่แค่ `console.log(...)` แต่ต้องสร้าง string เสร็จแล้ว `return result` กลับไปให้คนเรียกใช้ต่อได้
`return result` ทำให้เรานำผลลัพธ์ไปเก็บในตัวแปร ส่งต่อ หรือแสดงผลทีหลังได้ ไม่ถูกบังคับให้พิมพ์ทันทีใน function
function buildStarTriangle(rows) {
let result = "";
for (let row = 1; row <= rows; row++) {
let line = "";
for (let col = 0; col < row; col++) {
line += "*";
}
result += line;
if (row < rows) {
result += "\n";
}
}
return result;
}
const triangle = buildStarTriangle(4);
console.log(triangle);ข้อผิดพลาดที่พบบ่อยกับ nested loop
- **ลืม reset `line` ในแต่ละรอบของ loop นอก** — ถ้า `line` ถูกประกาศไว้นอก loop นอก ข้อความจะไหลสะสมผิดบรรทัด
- **เติม `\n` หลังบรรทัดสุดท้ายด้วย** — ทำให้ string ไม่ตรงกับผลลัพธ์ที่คาดหวังและตรวจเทียบ exact string ไม่ผ่าน
- **ใช้ตัวแปร loop ซ้ำกัน** — เช่นใช้ `i` ทั้ง loop นอกและใน ทำให้อ่านยากและเสี่ยงสับสน
- **ใช้ `console.log` ใน function แต่ไม่ `return`** — จะเห็นผลบน console แต่เอาค่าไปใช้ต่อไม่ได้
- **สลับบทบาท loop นอกกับ loop ใน** — ทำให้จำนวนบรรทัดหรือจำนวนดาวต่อบรรทัดผิดจากที่ตั้งใจ
ตัวอย่างผิดนี้รวมหลุมพรางที่เจอบ่อยไว้ในที่เดียว เพื่อให้เห็นว่าทำไมแต่ละจุดถึงสำคัญ
function wrongTriangle(rows) {
let result = "";
let line = "";
for (let row = 1; row <= rows; row++) {
for (let col = 0; col < row; col++) {
line += "*";
}
result += line + "\n";
}
console.log(result);
}
// ปัญหา:
// 1. line ไม่ถูก reset
// 2. มี newline เกินบรรทัดสุดท้าย
// 3. function ไม่ return ค่า