โหมดมืด
บทที่ 07 — Array และการจัดการข้อมูล
Array = รายการของข้อมูล — และการจัดการ array ด้วย map/filter/reduce คือทักษะที่ใช้บ่อยที่สุดในงานจริง
อ่านจบบทนี้คุณจะ:
- จัดการ array ได้คล่อง — เพิ่ม ลบ ค้นหา
- เข้าใจ
map,filter,reduceอย่างลึกซึ้ง (3 ตัวนี้สำคัญที่สุด) - เขียน "data pipeline" — ต่อ method หลายตัวเข้าด้วยกัน
- รู้จัก array methods ที่ใช้บ่อยทั้งหมด
ใช้เวลา 3 ชั่วโมง
Part 1: Array พื้นฐาน
1.1 Array คืออะไร
Array = รายการของข้อมูล เรียงตามลำดับ เข้าถึงด้วยตำแหน่ง (index)
javascript
const fruits = ["แอปเปิล", "กล้วย", "ส้ม"];จุดสำคัญ — index เริ่มนับจาก 0 ไม่ใช่ 1:
javascript
fruits[0] // "แอปเปิล" — ตัวแรก
fruits[1] // "กล้วย" — ตัวที่สอง
fruits[2] // "ส้ม" — ตัวที่สาม
fruits[3] // undefined — ไม่มีตัวที่ 4
fruits.length // 3 — จำนวนสมาชิก
fruits[fruits.length - 1] // "ส้ม" — ตัวสุดท้าย
fruits.at(-1) // "ส้ม" — วิธีใหม่ ใช้ index ติดลบได้array.at(-1) เป็นวิธีสมัยใหม่ที่อ่านง่ายกว่า array[array.length - 1] สำหรับเอาตัวท้าย
1.2 Array เก็บอะไรก็ได้
javascript
const mixed = [1, "ข้อความ", true, null, { name: "Alice" }, [1, 2]];แต่ในงานจริง — array ควรเก็บข้อมูล "ชนิดเดียวกัน" (เช่น array ของตัวเลขทั้งหมด, array ของ user object ทั้งหมด) เพื่อให้จัดการง่าย
1.3 สร้าง Array
javascript
const a = [1, 2, 3]; // ตรง ๆ (ใช้บ่อยสุด)
const b = Array.from("abc"); // จาก string → ["a","b","c"]
// เดี๋ยวจะเห็น (_, i) => ... โดย _ ไม่ใช่ syntax พิเศษของ JS แค่เป็นชื่อตัวแปรธรรมดา
// ที่โปรแกรมเมอร์ตกลงกันเป็น convention ว่า "ตัวแปรนี้รับมาแต่ไม่ใช้" — ตรงนี้แค่ต้องการ i (ตำแหน่ง) เท่านั้น
const c = Array.from({ length: 5 }, (_, i) => i); // [0,1,2,3,4]
const d = new Array(3).fill(0); // [0, 0, 0]Array.from({ length: n }, (_, i) => ...) เป็นเทคนิคสร้าง array ขนาด n ที่มีประโยชน์
Part 2: เพิ่ม / ลบ สมาชิก
2.1 Methods ที่ "แก้ของเดิม" (mutating)
methods กลุ่มนี้ เปลี่ยน array เดิม (mutating = แก้ของเดิม):
javascript
const arr = [1, 2, 3];
arr.push(4); // เพิ่มท้าย → [1, 2, 3, 4]
arr.pop(); // ลบท้าย → [1, 2, 3] (คืนค่าที่ลบ: 4)
arr.unshift(0); // เพิ่มหน้า → [0, 1, 2, 3]
arr.shift(); // ลบหน้า → [1, 2, 3] (คืนค่าที่ลบ: 0)splice — เพิ่ม/ลบ ตรงกลาง:
javascript
const arr = [1, 2, 3, 4, 5];
arr.splice(1, 2); // ลบ 2 ตัว เริ่มจาก index 1 → [1, 4, 5]
arr.splice(1, 0, "a", "b"); // เพิ่ม "a","b" ที่ index 1 (ลบ 0 ตัว)sort และ reverse — ก็แก้ array เดิม:
javascript
const arr = [3, 1, 2];
arr.sort(); // [1, 2, 3] — แต่ระวัง! (Part 8.1) — แก้ array เดิม! ใช้ toSorted() ถ้าไม่อยากแก้
arr.reverse(); // กลับลำดับ — แก้ array เดิม! ใช้ toReversed() ถ้าไม่อยากแก้2.2 Methods ที่ "ไม่แก้ของเดิม" (non-mutating)
💡 เราเรียน mutating methods ก่อนเพื่อให้เข้าใจพื้นฐาน แต่ในงานจริงแนะนำให้ใช้ non-mutating เป็นหลัก — เพราะปลอดภัยกว่าและคาดเดาได้ง่ายกว่า
JavaScript สมัยใหม่ (ES2023) มี methods ที่ไม่แก้ของเดิม — สร้างใหม่แทน:
javascript
const arr = [3, 1, 2];
const sorted = arr.toSorted(); // [1, 2, 3] — arr เดิมไม่เปลี่ยน
const reversed = arr.toReversed();
const updated = arr.with(0, 99); // เปลี่ยน index 0 เป็น 99 → array ใหม่📦 ต้องการเวอร์ชันไหน:
toSorted/toReversed/withเป็น ES2023 — รัน Node.js 20+, Chrome 110+, Firefox 115+, Safari 16+ ขึ้นไปถึงจะมี ถ้าโปรเจกต์ยังต้องรองรับเบราว์เซอร์/Node เก่ากว่านั้น ใช้สำนวน copy ก่อนค่อย mutate แทน เช่น[...arr].sort(),[...arr].reverse()
💡 ทำไม non-mutating ดี: การ "แก้ของเดิม" ทำให้เกิดบั๊ก (จำเรื่อง reference จากบทที่ 02 ได้ไหม — ถ้ามีหลายที่ชี้ array เดียวกัน แก้ที่หนึ่งกระทบหมด) — การ "สร้างใหม่" ปลอดภัยกว่า โค้ดคาดเดาได้ → งานสมัยใหม่นิยม non-mutating
Part 3: ค้นหาข้อมูลใน Array
javascript
const nums = [10, 20, 30, 40, 50];
nums.includes(30) // true — มี 30 ไหม
nums.indexOf(30) // 2 — 30 อยู่ตำแหน่งไหน (ไม่เจอคืน -1)
nums.find(n => n > 25) // 30 — ตัวแรกที่เข้าเงื่อนไข
nums.findIndex(n => n > 25) // 2 — index ของตัวแรกที่เข้าเงื่อนไข
nums.some(n => n > 45) // true — มีอย่างน้อย 1 ตัวที่ > 45 ไหม
nums.every(n => n > 5) // true — ทุกตัว > 5 ไหมอธิบาย:
includes/indexOf— หาด้วย "ค่า" ตรง ๆfind/findIndex— หาด้วย "เงื่อนไข" (ส่ง function เข้าไป)some— "มีบางตัวไหม" /every— "ทุกตัวไหม"
Part 4: map — แปลงทุกตัว
map คือ method ที่ใช้บ่อยที่สุด — "แปลงทุกตัวใน array เป็นค่าใหม่ ได้ array ใหม่ที่ยาวเท่าเดิม"
4.1 พื้นฐาน
javascript
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8]
console.log(numbers); // [1, 2, 3, 4] — ของเดิมไม่เปลี่ยนอธิบาย: map รับ function เข้าไป — มันเรียก function นั้นกับสมาชิกทุกตัว — แล้วเก็บผลลัพธ์เป็น array ใหม่
ลองคิดทีละขั้น:
text
[1, 2, 3, 4]
↓ ↓ ↓ ↓ แต่ละตัวผ่าน n => n * 2
[2, 4, 6, 8]4.2 ตัวอย่างใช้จริง
งานที่เจอบ่อยสุดของ map คือ "แปลงรูปข้อมูล" — เช่นมี array ของ object ผู้ใช้ แล้วอยากได้แค่ชื่อ หรือแปลงเป็นข้อความรูปแบบใหม่:
javascript
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
];
// ดึงเฉพาะชื่อ
const names = users.map(user => user.name);
console.log(names); // ["Alice", "Bob"]
// แปลงเป็นรูปแบบใหม่
const labels = users.map(user => `${user.name} (${user.age})`);
console.log(labels); // ["Alice (25)", "Bob (30)"]4.3 map มี index ให้ด้วย
function ใน map รับ argument ตัวที่ 2 เป็น index:
javascript
const items = ["a", "b", "c"];
const numbered = items.map((item, index) => `${index + 1}. ${item}`);
console.log(numbered); // ["1. a", "2. b", "3. c"]Part 5: filter — กรองเอาเฉพาะที่ต้องการ
filter = "เก็บเฉพาะตัวที่ผ่านเงื่อนไข ได้ array ใหม่ที่อาจสั้นกว่าเดิม"
5.1 พื้นฐาน
javascript
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4, 6]อธิบาย: filter รับ function ที่คืน true/false — ถ้า function คืน true สำหรับตัวไหน → เก็บตัวนั้นไว้
text
[1, 2, 3, 4, 5, 6]
✗ ✓ ✗ ✓ ✗ ✓ แต่ละตัวผ่าน n => n % 2 === 0
[2, 4, 6]5.2 ตัวอย่างใช้จริง
งานจริงที่ใช้ filter บ่อยคือคัดข้อมูลตามหลายเงื่อนไข — เช่นเอาเฉพาะผู้ใช้ที่ active และอายุถึงเกณฑ์:
javascript
const users = [
{ name: "Alice", age: 25, active: true },
{ name: "Bob", age: 17, active: true },
{ name: "Carol", age: 30, active: false },
];
const activeAdults = users.filter(u => u.active && u.age >= 18);
console.log(activeAdults); // [{ name: "Alice", ... }]5.3 map กับ filter ต่างกันยังไง — จำให้แม่น
map— แปลงทุกตัว → array ใหม่ ยาวเท่าเดิมเสมอfilter— เก็บบางตัว → array ใหม่ อาจสั้นกว่าหรือเท่าเดิม
Part 6: reduce — ยุบ array เป็นค่าเดียว
reduce คือ method ที่ทรงพลังที่สุด — และยากที่สุดสำหรับมือใหม่ — มันใช้ "ยุบ array ทั้งหมดให้เหลือค่าเดียว"
6.1 ตัวอย่างง่ายสุด — หาผลรวม
javascript
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((total, n) => total + n, 0);
console.log(sum); // 10อธิบายทีละขั้น — reduce มี 2 ส่วน:
- function
(total, n) => total + n— "เอาผลสะสมรวมกับตัวปัจจุบัน" 0— ค่าเริ่มต้นของผลสะสม
ลองเดินทีละรอบ:
text
ค่าเริ่มต้น total = 0
รอบ 1: total=0, n=1 → 0 + 1 = 1 → total เป็น 1
รอบ 2: total=1, n=2 → 1 + 2 = 3 → total เป็น 3
รอบ 3: total=3, n=3 → 3 + 3 = 6 → total เป็น 6
รอบ 4: total=6, n=4 → 6 + 4 = 10 → total เป็น 10
ผลลัพธ์: 10total คือ "ผลสะสม" ที่ส่งต่อจากรอบหนึ่งไปอีกรอบ
💡 เปรียบเทียบ: reduce เหมือน "ก้อนหิมะกลิ้งลงเขา" — มันสะสมขึ้นเรื่อย ๆ จากตัวแรกถึงตัวสุดท้าย
📌 ถ้ายังงง reduce: ลองทำ Lab 1 ก่อน แล้วค่อยกลับมาอ่านอีกรอบ — reduce เป็นเรื่องที่ต้องใช้เวลา ไม่ต้องกังวลถ้ายังไม่เข้าใจทันที
6.2 reduce ทำได้มากกว่าหาผลรวม
reduce ยุบเป็น "อะไรก็ได้" — ไม่จำเป็นต้องเป็นตัวเลข
ยุบเป็น object — นับความถี่:
javascript
const words = ["a", "b", "a", "c", "a", "b"];
const count = words.reduce((acc, word) => {
acc[word] = (acc[word] ?? 0) + 1;
return acc;
}, {}); // ค่าเริ่มต้นเป็น object ว่าง
console.log(count); // { a: 3, b: 2, c: 1 }หาค่ามากสุด:
javascript
const nums = [5, 2, 9, 1, 7];
// ✅ วิธีที่แนะนำ — ใส่ -Infinity เป็นค่าเริ่มต้นเสมอ ปลอดภัยแม้ array ว่าง
const max = nums.reduce((m, n) => n > m ? n : m, -Infinity);
console.log(max); // 9💡 ทำไมใช้
-Infinityเป็นค่าเริ่มต้น? — เพราะตัวเลขทุกตัวมากกว่า-Infinityเสมอ ทำให้รอบแรกได้สมาชิกตัวแรกเป็นmโดยอัตโนมัติ และถ้า array ว่าง ก็จะได้-Infinityกลับมาแทนที่จะ throw Error
💡 ทางเลือกสั้นกว่าสำหรับหาค่าสูงสุด:
Math.max(...nums)— แต่จะได้-Infinityถ้า array ว่าง ซึ่งถือว่าสมเหตุสมผล
ยุบ array ของ object เป็นยอดรวม:
javascript
const cart = [
{ name: "หนังสือ", price: 200 },
{ name: "ปากกา", price: 30 },
{ name: "สมุด", price: 50 },
];
const total = cart.reduce((sum, item) => sum + item.price, 0);
console.log(total); // 2806.3 ⚠️ อย่าลืมค่าเริ่มต้น
javascript
[1, 2, 3].reduce((a, b) => a + b, 0); // ✅ มีค่าเริ่มต้น 0
[].reduce((a, b) => a + b); // ❌ Error — array ว่าง + ไม่มีค่าเริ่มต้น
[].reduce((a, b) => a + b, 0); // ✅ 0→ ใส่ค่าเริ่มต้นเสมอ — ปลอดภัยกว่า
Part 7: ต่อ Method เข้าด้วยกัน — Data Pipeline (สายท่อข้อมูล)
📌 data pipeline = "สายท่อข้อมูล" — เอาข้อมูลใส่ปลายทางหนึ่ง แล้วต่อขั้นตอนแปลง/กรอง/รวมเป็นทอด ๆ จนได้ผลลัพธ์ปลายอีกข้าง
พลังจริง ๆ ของ map/filter/reduce คือ — เอามาต่อกันเป็นทอด ๆ
ตัวอย่าง — "หายอดรวมของสินค้าที่ราคาเกิน 50 บาท หลังบวก VAT 7%":
javascript
const products = [
{ name: "A", price: 100 },
{ name: "B", price: 30 },
{ name: "C", price: 80 },
{ name: "D", price: 45 },
];
const total = products
.filter(p => p.price > 50) // 1. กรองเอาที่ราคา > 50 → [A(100), C(80)]
.map(p => p.price * 1.07) // 2. บวก VAT แต่ละตัว → [107, 85.6]
// A: 100 * 1.07 = 107 / C: 80 * 1.07 = 85.6
.reduce((sum, price) => sum + price, 0); // 3. รวม → 107 + 85.6 = 192.6
console.log(total); // 192.6อ่านโค้ดนี้เหมือนอ่านประโยค: "เอา products → กรองที่ราคาเกิน 50 → บวก VAT → รวมทั้งหมด"
นี่คือสไตล์ "functional / declarative" — เราบอก "อยากได้อะไร" ไม่ใช่ "ทำยังไงทีละขั้น" — โค้ดอ่านง่ายกว่า loop ธรรมดามาก
📌 declarative vs imperative: declarative = บอกว่าอยากได้อะไร (เช่น "กรอง > 50 แล้วบวก VAT แล้วรวม") ส่วน imperative = บอกขั้นตอนทำยังไง (เช่น "สร้างตัวแปร total, วน loop, ตรวจเงื่อนไข, บวกเข้า total") — สำนวน
map/filter/reduceเป็น declarative ในขณะที่for/whileเป็น imperative
เทียบกับเขียนด้วย loop:
javascript
// แบบ loop — ยาวกว่า อ่านยากกว่า
let total = 0;
for (const p of products) {
if (p.price > 50) {
total += p.price * 1.07;
}
}Part 8: Array Methods อื่น ๆ ที่ใช้บ่อย
8.1 sort — เรียงลำดับ (ระวังกับดัก!)
javascript
[3, 1, 2].sort(); // [1, 2, 3] — บังเอิญถูก! เพราะตัวเลขหลักเดียวเรียง unicode ตรงกับลำดับตัวเลขพอดี
[10, 2, 1].sort(); // [1, 10, 2] — ⚠️ ผิด!ทำไม [10, 2, 1] เรียงผิด? — เพราะ sort แบบไม่ใส่อะไร มันแปลงทุกตัวเป็น string แล้วเรียงตามตัวอักษร — "10" มาก่อน "2" (เพราะ "1" < "2")
→ เรียงตัวเลข ต้องใส่ compare function (= ฟังก์ชันเปรียบเทียบสองค่า ว่าตัวไหนควรมาก่อน):
javascript
[10, 2, 1].sort((a, b) => a - b); // [1, 2, 10] — น้อยไปมาก
[10, 2, 1].sort((a, b) => b - a); // [10, 2, 1] — มากไปน้อยกฎของ compare function: คืนเลขลบ = a มาก่อน, คืนเลขบวก = b มาก่อน
เรียง object:
javascript
users.sort((a, b) => a.age - b.age); // เรียงตามอายุ
users.sort((a, b) => a.name.localeCompare(b.name)); // เรียงตามชื่อ⚠️ sort แก้ array เดิม — ถ้าไม่อยากให้เดิมเปลี่ยน ใช้ toSorted (Part 2.2)
8.2 flat — แผ่ array ซ้อน
flat "แผ่" array ที่ซ้อนกันให้แบนลงหนึ่งชั้น (หรือใส่ Infinity เพื่อแผ่ทุกชั้น) — มีประโยชน์เวลารวม array ของ array เข้าด้วยกัน:
javascript
[1, [2, 3], [4, [5]]].flat(); // [1, 2, 3, 4, [5]]
[1, [2, [3, [4]]]].flat(Infinity); // [1, 2, 3, 4] — แผ่ทุกชั้น8.3 flatMap — map แล้ว flat
flatMap คือ map แล้วตามด้วย flat หนึ่งชั้นในขั้นตอนเดียว — เหมาะเวลาที่ฟังก์ชัน map คืน array (เช่นแตกประโยคเป็นคำ) แล้วอยากได้ผลลัพธ์แบนเป็น array เดียว:
javascript
const sentences = ["hello world", "foo bar"];
const words = sentences.flatMap(s => s.split(" "));
console.log(words); // ["hello", "world", "foo", "bar"]8.4 slice — ตัดเอาบางส่วน (ไม่แก้ของเดิม)
javascript
const arr = [1, 2, 3, 4, 5];
arr.slice(1, 3) // [2, 3] — เอา index 1 ถึง 3 (ไม่รวม 3)
arr.slice(2) // [3, 4, 5] — จาก index 2 ถึงท้าย
arr.slice(-2) // [4, 5] — 2 ตัวท้าย⚠️ อย่าสับสน slice (ตัด ไม่แก้เดิม) กับ splice (เพิ่ม/ลบ แก้เดิม)
8.5 join — รวม array เป็น string
join รวมสมาชิกทุกตัวของ array เป็นข้อความเดียว โดยคั่นด้วยตัวอักษรที่กำหนด — ตรงข้ามกับ split ของ string:
javascript
["a", "b", "c"].join("-") // "a-b-c"
["a", "b", "c"].join(", ") // "a, b, c"8.6 forEach — วนทำงาน (ทบทวนจากบทที่ 04)
javascript
[1, 2, 3].forEach(n => console.log(n));forEach ใช้ "ทำอะไรกับแต่ละตัว" (side effect = ผลข้างเคียง เช่นไปแก้ตัวแปรข้างนอก, print, ส่ง HTTP) — ไม่คืนค่า. ถ้าต้องการ array ใหม่ → ใช้ map
Part 9: เทคนิคที่ใช้บ่อยในงานจริง
9.1 ตัดค่าซ้ำ
ตัดค่าซ้ำออกจาก array ด้วยการแปลงผ่าน Set แล้ว spread กลับ — สำนวนสั้นที่สุดและนิยมที่สุด:
javascript
const arr = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(arr)]; // [1, 2, 3] (จำ Set จากบทที่ 06)9.2 หาผลรวม / ค่าเฉลี่ย
หาผลรวมด้วย reduce (สะสมทีละตัว) แล้วหารด้วยจำนวนสมาชิกเพื่อได้ค่าเฉลี่ย — งานพื้นฐานที่เจอตลอด:
javascript
const scores = [85, 90, 78, 92];
const sum = scores.reduce((a, b) => a + b, 0);
const average = sum / scores.length;9.3 จัดกลุ่ม (group by)
การจัดกลุ่มข้อมูลตามค่าใดค่าหนึ่ง (เช่นกลุ่มคนตามเมือง) ทำได้ด้วย reduce สะสมเข้า object — หรือใช้ Object.groupBy ตัวใหม่ (ES2024) ที่สั้นกว่า:
javascript
const people = [
{ name: "Alice", city: "Bangkok" },
{ name: "Bob", city: "Tokyo" },
{ name: "Carol", city: "Bangkok" },
];
const byCity = people.reduce((groups, person) => {
const city = person.city;
groups[city] = groups[city] ?? [];
groups[city].push(person);
return groups;
}, {});
// { Bangkok: [Alice, Carol], Tokyo: [Bob] }หรือใช้ Object.groupBy (ES2024 — ใหม่):
javascript
const byCity = Object.groupBy(people, person => person.city);
console.log(byCity);
// {
// Bangkok: [{ name: "Alice", city: "Bangkok" }, { name: "Carol", city: "Bangkok" }],
// Tokyo: [{ name: "Bob", city: "Tokyo" }]
// }📦
Object.groupByเป็น ES2024 — ต้อง Node.js 21+, Chrome 117+, Firefox 119+, Safari 17.4+ ถ้ารันที่เก่ากว่านี้ ใช้สำนวนreduceด้านบนแทน
🔭 มองไปข้างหน้า — Iterator helpers (Stage 4, ตรวจสอบสถานะ/เวอร์ชันล่าสุดก่อนใช้): TC39 รับ proposal
Iterator.prototype.map/filter/take/...เข้า Stage 4 แล้ว ทำให้ใช้map/filterกับ iterator ได้ตรง ๆ โดยไม่ต้องสร้าง array กลาง — รองรับใน Node.js 22+/Chrome 122+ ขึ้นไป ส่วนเบราว์เซอร์อื่นให้ตรวจสอบสถานะล่าสุดก่อนใช้จริง (เช่นarray.values().filter(...).map(...).toArray()) เหมาะกับ lazy pipeline (= ประมวลผลทีละตัวตามต้องการ ไม่สร้าง array ทั้งก้อนไว้ล่วงหน้า) ที่ข้อมูลเยอะ ๆ
9.4 เช็คว่า array ว่างไหม
เช็คว่า array ว่างหรือมีของด้วยการดู .length — JS ไม่มีเมธอด isEmpty() ในตัว:
javascript
if (arr.length === 0) { ... } // ว่าง
if (arr.length > 0) { ... } // มีของPart 10: Lab — ลงมือทำ
Lab 1: map / filter / reduce
ฝึกต่อ filter → map → reduce เป็นสายเดียว (method chaining = ต่อ method เป็นทอด ๆ ด้วย . — ผลลัพธ์ของอันก่อนเป็น input ของอันถัดไป) — รูปแบบที่ทรงพลังที่สุดของการจัดการข้อมูลใน JS โจทย์: หายอดรวมเฉพาะออเดอร์ที่สำเร็จ:
javascript
const orders = [
{ id: 1, amount: 250, status: "completed" },
{ id: 2, amount: 100, status: "cancelled" },
{ id: 3, amount: 400, status: "completed" },
{ id: 4, amount: 150, status: "completed" },
];
// ยอดรวมของ order ที่ completed เท่านั้น
const totalCompleted = orders
.filter(o => o.status === "completed")
.map(o => o.amount)
.reduce((sum, amount) => sum + amount, 0);
console.log(totalCompleted); // 800Lab 2: หา top N
ฝึกเขียนฟังก์ชันทั่วไปหา "อันดับสูงสุด N ตัว" โดย copy array ก่อน sort (ไม่แก้ของเดิม) แล้ว slice — รวม sort + slice + การไม่ทำลายข้อมูลต้นฉบับ:
javascript
function topN(items, key, n) {
return [...items] // copy ก่อน (ไม่แก้ของเดิม)
.sort((a, b) => b[key] - a[key])
.slice(0, n);
}
const students = [
{ name: "A", score: 75 },
{ name: "B", score: 92 },
{ name: "C", score: 88 },
{ name: "D", score: 60 },
];
console.log(topN(students, "score", 2));
// [{ name: "B", score: 92 }, { name: "C", score: 88 }]Lab 3: นับความถี่ของคำ
ฝึกรวมหลายเทคนิคเป็นสายเดียว — toLowerCase → split → filter → reduce เพื่อนับว่าคำไหนปรากฏกี่ครั้ง:
📝 ตัวอย่างนี้ใช้ regex
/\s+/ซึ่งหมายถึง "ช่องว่าง 1 ตัวขึ้นไป (เว้นวรรค, tab, ขึ้นบรรทัด)" — เพื่อแยกคำ ส่วน regex ละเอียดเรียนกันบทที่ 08 ตรงนี้แค่จำว่า\s+= ช่องว่าง 1 ตัวขึ้นไป ก็พอ
javascript
function wordCount(text) {
return text
.toLowerCase()
.split(/\s+/) // แยกคำด้วย regex (เรียนละเอียดบทที่ 08)
.filter(word => word.length > 0)
.reduce((count, word) => {
count[word] = (count[word] ?? 0) + 1;
return count;
}, {});
}
console.log(wordCount("the cat the dog the cat"));
// { the: 3, cat: 2, dog: 1 }Lab 4: แปลงข้อมูล
javascript
const apiData = [
{ user_id: 1, full_name: "Alice Smith", is_active: true },
{ user_id: 2, full_name: "Bob Jones", is_active: false },
];
const cleaned = apiData
.filter(u => u.is_active)
.map(u => ({
id: u.user_id,
name: u.full_name,
}));
console.log(cleaned); // [{ id: 1, name: "Alice Smith" }]Part 11: Checkpoint
- Array index เริ่มนับจากเลขอะไร?
array.at(-1)ทำอะไร?- Mutating vs non-mutating methods ต่างกันยังไง? ทำไม non-mutating ดีกว่า?
mapต่างfilterยังไง? (เรื่องความยาวของ array ผลลัพธ์)reduceทำงานยังไง? ค่าเริ่มต้นสำคัญยังไง?findต่างfilterยังไง?someต่างeveryยังไง?- ทำไม
[10, 2, 1].sort()เรียงผิด? แก้ยังไง? sliceต่างspliceยังไง?- ตัดค่าซ้ำออกจาก array ทำยังไง?
Part 12: สรุปบทนี้
- Array = รายการข้อมูล — index เริ่มจาก 0
- Mutating (push, pop, sort, splice) แก้ของเดิม / non-mutating (map, filter, slice, toSorted) สร้างใหม่ — นิยมแบบไม่แก้ของเดิม
map— แปลงทุกตัว → array ยาวเท่าเดิมfilter— เก็บที่ผ่านเงื่อนไข → array อาจสั้นกว่าreduce— ยุบเป็นค่าเดียว (ผลรวม, object, อะไรก็ได้) — อย่าลืมค่าเริ่มต้น- ต่อ method เข้าด้วยกัน = data pipeline ที่อ่านง่ายกว่า loop
sortต้องใส่ compare function สำหรับตัวเลข[...new Set(arr)]= ตัดค่าซ้ำ
บทต่อไป — String, Regex, และวันเวลา