JavaScript
Arrays
concat
เรียนรู้ concat() — method รวม array โดยไม่เปลี่ยนต้นฉบับ ตั้งแต่การรวม 2 array ได้ array ใหม่, การรวมหลาย array และค่าเดี่ยวพร้อมกัน, พฤติกรรม flatten แค่ระดับเดียวกับ nested array, การใช้ concat() เพื่อ copy array แบบ shallow, ไปจนถึงการเลือกใช้ concat vs push ให้เหมาะกับสถานการณ์ ผ่าน Lab 3 ข้อ
`concat()` คืออะไร — รวม array โดยไม่เปลี่ยนต้นฉบับ
concat() เป็น method ใน array ใช้รวม (merge) array ตั้งแต่ 2 ตัวขึ้นไปเข้าด้วยกันเป็น array เดียว — และคืนค่าเป็น array ใหม่ โดยไม่เปลี่ยน array ต้นฉบับ สิ่งที่ต้องรู้: • คืนค่า (return) เป็น array ใหม่ — array เก่าไม่เปลี่ยน • รับ argument ได้หลายตัว — จะเป็น array หรือค่าธรรมดา (string, number, ...) ก็ได้ • ถ้า argument เป็น array — element แต่ละตัวใน array นั้นจะถูกเพิ่มเข้าไปในผลลัพธ์ (ไม่ใช่ทั้ง array) • ถ้า argument เป็นค่าธรรมดา — ค่านั้นจะถูกเพิ่มเข้าไปในผลลัพธ์โดยตรง • concat() เปลี่ยนวิธีการทำงานจาก method ก่อนหน้านี้ (push/pop/shift/unshift) ที่ mutate ต้นฉบับทั้งหมด
const fruits = ["แอปเปิ้ล", "กล้วย"];
const veggies = ["ผักกาด", "มะเขือเทศ"];
// concat() คืนค่า array ใหม่
const all = fruits.concat(veggies);
console.log(all); // ["แอปเปิ้ล", "กล้วย", "ผักกาด", "มะเขือเทศ"]
console.log(fruits); // ["แอปเปิ้ล", "กล้วย"] — ต้นฉบับไม่เปลี่ยน ✓
console.log(veggies); // ["ผักกาด", "มะเขือเทศ"] — ต้นฉบับไม่เปลี่ยน ✓จุดสำคัญที่เห็นจากตัวอย่าง: fruits และ veggies ยังเหมือนเดิม — concat() ไม่ mutate array ต้นฉบับเลย ต่างจาก push() ซึ่งเปลี่ยน array ต้นฉบับโดยตรง นี่คือความแตกต่างใหญ่ที่สุดระหว่าง concat() กับ method ก่อนหน้านี้ (push/pop/shift/unshift) — concat() เป็น method แรกที่เราได้เรียนที่สร้าง array ใหม่แทนที่จะเปลี่ยนของเก่า
| เรื่อง | concat() | push() |
|---|---|---|
| return | array ใหม่ที่รวมกันแล้ว | length ใหม่ของ array (number) |
| เปลี่ยนต้นฉบับ? | ไม่ — array ต้นฉบับไม่เปลี่ยน | ใช่ — เพิ่ม element ต่อท้ายต้นฉบับโดยตรง |
| รับ argument | รับกี่ตัวก็ได้ — array หรือค่าเดี่ยว | รับกี่ตัวก็ได้ — แต่เป็นค่าที่จะเพิ่มต่อท้าย |
| ใช้เมื่อไหร่ | ต้องการรวมหลาย array โดยเก็บของเก่าไว้ | ต้องการเพิ่มของต่อท้าย array เดิม |
const a = [1, 2];
const b = [3, 4];
// === concat() — สร้าง array ใหม่
const merged = a.concat(b);
console.log(merged); // [1, 2, 3, 4] ← array ใหม่
console.log(a); // [1, 2] ← a ไม่เปลี่ยน
// === push() — mutate array ต้นฉบับ + return length
const length = a.push(3, 4);
console.log(length); // 4 ← push return length ไม่ใช่ array
console.log(a); // [1, 2, 3, 4] ← a เปลี่ยนไปแล้ว
// concat ยังทำงานกับหลาย array ได้สะดวกกว่า
const c = [5, 6];
const all = a.concat(b, c);
console.log(all); // [1, 2, 3, 4, 5, 6]กฎการเลือกใช้: • ต้องการเพิ่มของต่อท้าย array เดิมและอัปเดตค่าเลย → push() • ต้องการรวมหลาย array โดยเก็บข้อมูลเดิมไว้ทั้งหมด → concat() • concat() ยังทำงานได้ดีเมื่อต้องการรวมมากกว่า 2 array — ใส่ argument ได้หลายตัวพร้อมกัน
รวมหลาย array พร้อมกัน และผสมค่าเดี่ยว ๆ
concat() รับ argument ได้หลายตัว — และแต่ละ argument จะเป็น array หรือค่าธรรมดาก็ได้ กฎการรวม: • ถ้า argument เป็น array → element แต่ละตัวใน array นั้นจะถูกเพิ่มเข้าไปในผลลัพธ์ (array จะถูกแกะออกหนึ่งระดับ) • ถ้า argument เป็นค่าเดี่ยว (string, number, boolean, ...) → ค่านั้นจะถูกเพิ่มเข้าไปในผลลัพธ์โดยตรง • ลำดับในผลลัพธ์เรียงตามลำดับ argument ที่เราใส่
const teamA = ["สมชาย", "สมศรี"];
const teamB = ["วิชัย", "สุดา"];
const teamC = ["ประเสริฐ"];
// รวม 3 array พร้อมกัน
const everyone = teamA.concat(teamB, teamC);
console.log(everyone);
// ["สมชาย", "สมศรี", "วิชัย", "สุดา", "ประเสริฐ"]
// === ผสม array กับค่าเดี่ยว
const base = [1, 2];
const result = base.concat([3, 4], 5, [6]);
// array → element ถูกแกะ
// ค่าเดี่ยว → เพิ่มตรง ๆ
// array → element ถูกแกะ
console.log(result); // [1, 2, 3, 4, 5, 6]
// === ใช้ concat ต่อ string
const chars = ["a", "b"];
const more = chars.concat("c", "d");
console.log(more); // ["a", "b", "c", "d"]
// string "c" และ "d" เป็นค่าเดี่ยว — เพิ่มตรง ๆ ไม่ใช่อาร์เรย์// ใช้ concat จริง — รวม categories จากหลายแหล่ง
const userTags = ["javascript", "react"];
const articleTags = ["array", "method"];
const extraTag = "programming";
// รวมทุก category เข้าด้วยกัน
const allTags = userTags.concat(articleTags, extraTag);
console.log(allTags);
// ["javascript", "react", "array", "method", "programming"]
// === ใช้ประโยชน์: ตรวจสอบว่า tag ที่ต้องการมีอยู่หรือไม่
if (allTags.includes("array")) {
console.log("มี tag 'array' ในรายการรวม ✓");
}
// === รวม array เปล่าก็ได้ — ไม่มีผลอะไร
const baseTags = ["html", "css"];
const extra = []; // array เปล่า
const result = baseTags.concat(extra, "js");
console.log(result); // ["html", "css", "js"]
// extra (array เปล่า) — ไม่เพิ่มอะไร แต่ลำดับอื่นปกติข้อควรรู้: • concat() กับ array เปล่า — array เปล่าจะไม่เพิ่มอะไรในผลลัพธ์ ตัวอื่น ๆ ยังเรียงตามปกติ • ลำดับของ element ใน array แต่ละตัว — element จาก array แรกจะมาก่อน element จาก array ต่อไป ตามลำดับ argument • concat() สามารถรับ argument จำนวนเท่าไหร่ก็ได้ — ไม่จำกัดแค่ 2 ตัว
`concat()` กับ nested array — flatten แค่ระดับเดียว
concat() มีพฤติกรรมสำคัญที่ต้องรู้: เมื่อ argument เป็น array — concat จะแกะ (flatten) array นั้นแค่ 1 ระดับเท่านั้น หมายความว่า: • [1, 2].concat([3, 4]) → [1, 2, 3, 4] — array ถูกแกะ 1 ระดับ • [1, 2].concat([[3, 4]]) → [1, 2, [3, 4]] — nested array ไม่ถูกแกะต่อ concat() ไม่ใช่ deep flatten — มันแกะแค่ array ที่เป็น argument โดยตรงเท่านั้น ไม่แกะ array ที่ซ้อนอยู่ข้างใน
const a = [1, 2];
// === array ธรรมดา → element ถูกแกะ
console.log(a.concat([3, 4]));
// [1, 2, 3, 4] ✓
// === array ซ้อน 1 ชั้น → ถูกแกะแค่ชั้นนอก
console.log(a.concat([[3, 4]]));
// [1, 2, [3, 4]] ← nested array [3,4] ยังอยู่ ไม่ถูกแกะต่อ
// === array ซ้อน 2 ชั้น → แกะชั้นนอกเท่านั้น
console.log(a.concat([[[3, 4]]]));
// [1, 2, [[3, 4]]] ← ชั้นในสุดยังอยู่
// === ใช้ concat() กับ mixed — array ธรรมดากับ nested
const flat = [1, 2];
const nested = [[3, 4], [5, 6]];
const result = flat.concat(nested);
console.log(result);
// [1, 2, [3, 4], [5, 6]]
// nested array แต่ละตัวเป็น element อยู่ — concat ไม่แกะต่อสรุปพฤติกรรมการแกะของ concat(): • argument เป็น [3, 4] → element 3 และ 4 ถูกเพิ่มแยกกัน (แกะ 1 ระดับ) • argument เป็น [[3, 4]] → element [3, 4] (ทั้ง array) ถูกเพิ่ม — array ชั้นในไม่ถูกแกะ • ถ้าต้องการ flatten array ซ้อนหลายชั้น → ต้องใช้ flat() หรือ flat(Infinity) (จะเรียนในบท array methods) จำง่าย ๆ: concat() แกะ array แค่ครั้งเดียว — เหมาะกับการรวม array ธรรมดา ไม่เหมาะกับการยุบ nested array
ใช้ `concat()` เพื่อ copy array
เทคนิคที่เจอบ่อย: เรียก concat() โดยไม่ส่ง argument — จะได้ shallow copy ของ array ต้นฉบับ `arr.concat()` สร้าง array ใหม่ที่มี element เหมือน arr ทุกประการ — มีประโยชน์เมื่อต้องการ copy array ก่อนจะ mutate วิธีนี้ทำงานได้เพราะ concat() สร้าง array ใหม่เสมอ ถึงแม้จะไม่มี argument ให้รวมก็ตาม
const original = ["a", "b", "c"];
// concat() โดยไม่ส่ง argument → copy array
const copied = original.concat();
console.log(copied); // ["a", "b", "c"] — เหมือน original
console.log(copied === original); // false — เป็น array คนละตัวกัน
// === พิสูจน์ว่าเป็นคนละ array — แก้ copied ไม่กระทบ original
copied.push("d");
console.log(copied); // ["a", "b", "c", "d"]
console.log(original); // ["a", "b", "c"] — original ไม่เปลี่ยน ✓
// === ใช้ [].concat(arr) ก็ได้ผลเหมือนกัน
const anotherCopy = [].concat(original);
console.log(anotherCopy); // ["a", "b", "c"]ข้อควรรู้: concat() ทำ shallow copy — ถ้า array มี object หรือ nested array ข้างใน ค่าเหล่านั้นจะถูก copy แค่ reference (ไม่ได้ deep copy) shallow copy หมายความว่า: • primitive value (string, number) — ถูก copy ค่า • object, array — ถูก copy reference (การแก้ไขค่าใน object ของ copy จะกระทบ original ด้วย) สำหรับการ copy แบบไม่กระทบกันทุกชั้น — ใช้ structuredClone() หรือวิธีอื่น (จะเรียนในบทถัดไป)
จุดที่มือใหม่มักพลาด
- concat() คืนค่า array ใหม่ — ไม่ mutate ต้นฉบับ — มือใหม่ที่คุ้นกับ push() มักเขียน `arr.concat(other)` แล้วคิดว่า arr เปลี่ยนไป — แต่ concat ต้อง capture return: `const result = arr.concat(other)` เสมอ
- concat() ไม่ใช่ push() — push return length (number) concat return array — ใช้ผิดวิธีอาจได้ผลลัพธ์แปลก ๆ เช่น console.log(push) เห็นเลข console.log(concat) เห็น array
- concat() แกะ array แค่ 1 ระดับ — `[1].concat([[2, 3]])` → `[1, [2, 3]]` ไม่ใช่ `[1, 2, 3]` — มือใหม่มักคาดหวัง deep flatten
- concat() กับ array เปล่า — array เปล่าไม่เพิ่มอะไรในผลลัพธ์ แต่ identity ของ concat ยังคงสร้าง array ใหม่ — `[1, 2].concat([])` → `[1, 2]` (array ใหม่)
- shallow copy จาก concat() — `arr.concat()` copy แค่ชั้นนอก object/array ข้างในยังใช้ reference เดิม — ถ้าคาดหวัง deep copy จะเจอ bug
- ลำดับของ element — element จาก array แรกมาก่อน array ต่อไปเสมอ ตามลำดับ argument — concat(arr1, arr2) ≠ concat(arr2, arr1) ในแง่ลำดับ