Article · programming
JavaScript Array Methods ขั้นสูง
JavaScript
เรียนรู้ Array Methods ที่ใช้บ่อยใน JavaScript ได้แก่ map, filter, reduce, find, some และ every พร้อมตัวอย่างโค้ด การเปรียบเทียบ และแบบฝึกหัดประยุกต์ใช้กับข้อมูลจริง
EP11: จัดการ Array อย่างมืออาชีพด้วย Array Methods
จากตอนก่อนที่เราเรียนรู้เรื่อง Scope และ Closure ใน JavaScript ตอนนี้เราจะกลับมาทำงานกับข้อมูลที่พบได้แทบทุกโปรแกรม นั่นคือ Array หรืออาร์เรย์ ซึ่งใช้เก็บข้อมูลหลายรายการไว้ในตัวแปรเดียว เช่น รายชื่อสินค้า คะแนนนักเรียน รายการคำสั่งซื้อ หรือข้อมูลสมาชิก
เมื่อข้อมูลมีจำนวนมาก การใช้ `for` หรือ `for...of` เพื่อวนตรวจสอบทีละรายการยังเป็นวิธีที่ดี แต่ JavaScript มี Array Methods ที่ช่วยให้โค้ดสั้นลง อ่านง่ายขึ้น และสื่อความตั้งใจได้ชัดเจนกว่า โดยเฉพาะ `map()`, `filter()`, `reduce()`, `find()`, `some()` และ `every()`
เป้าหมายของตอนนี้เมื่อเรียนจบ คุณจะเลือกใช้ Array Method ให้เหมาะกับงานได้ แยกแยะการแปลงข้อมูล การคัดกรอง การค้นหา และการสรุปผล พร้อมระวังเรื่องการแก้ไข Array ต้นฉบับ
1. เตรียมข้อมูลสำหรับทดลอง
สมมติว่าเรากำลังพัฒนาระบบร้านค้าออนไลน์ และมีข้อมูลสินค้าแต่ละรายการเป็น Object ภายใน Array สินค้าแต่ละชิ้นมีชื่อ ราคา และจำนวนคงเหลือ เราจะใช้ข้อมูลชุดเดียวกันเพื่อเปรียบเทียบวิธีทำงานของแต่ละ Method
const products = [
{ id: 1, name: 'สมุด', price: 35, stock: 20 },
{ id: 2, name: 'ปากกา', price: 12, stock: 0 },
{ id: 3, name: 'กระเป๋า', price: 450, stock: 8 },
{ id: 4, name: 'ดินสอ', price: 8, stock: 50 },
{ id: 5, name: 'ขวดน้ำ', price: 120, stock: 5 }
];
console.log('จำนวนสินค้า:', products.length);
console.log('สินค้าแรก:', products[0].name);
Array `products` มีข้อมูล 5 รายการ แต่ละรายการเป็น Object ที่มีคุณสมบัติแตกต่างกันตามค่า `price` และ `stock` ต่อไปเราจะเลือกเฉพาะข้อมูลที่ต้องการโดยไม่จำเป็นต้องเขียนลูปใหม่ทุกครั้ง
2. map() แปลงข้อมูลทุกสมาชิก
`map()` ใช้เรียกฟังก์ชันกับสมาชิกทุกตัวใน Array แล้วสร้าง Array ใหม่จากค่าที่ฟังก์ชันส่งกลับ เหมาะกับการแปลงรูปแบบข้อมูล เช่น ดึงเฉพาะชื่อสินค้า คำนวณราคาหลังหักส่วนลด หรือสร้างข้อความสำหรับแสดงผล
const names = products.map(product => product.name);
const discountedProducts = products.map(product => ({
name: product.name,
originalPrice: product.price,
discountedPrice: product.price * 0.9
}));
console.log(names);
console.log(discountedProducts);
ผลลัพธ์ของ `names` จะเป็น Array ของชื่อสินค้า ส่วน `discountedProducts` จะเป็น Array ของ Object รูปแบบใหม่ โดยราคาหลังหักส่วนลดเท่ากับ 90% ของราคาเดิม การใช้ `map()` ไม่ได้เปลี่ยน Array `products` โดยอัตโนมัติ เพราะเราเพียงอ่านค่าแล้วสร้างผลลัพธ์ใหม่
จำง่าย ๆใช้ `map()` เมื่อคุณต้องการผลลัพธ์หนึ่งค่าต่อสมาชิกเดิมหนึ่งรายการ เช่น มีสินค้า 5 ชิ้น ก็จะได้ผลลัพธ์ 5 รายการ หากฟังก์ชันคืนค่า `undefined` ผลลัพธ์ก็ยังมีตำแหน่งนั้นอยู่ แต่ค่าจะเป็น `undefined`
3. filter() คัดกรองสมาชิกที่ผ่านเงื่อนไข
`filter()` ใช้ตรวจสอบสมาชิกทีละตัว และเก็บเฉพาะสมาชิกที่ฟังก์ชันคืนค่าเป็นจริง เหมาะกับการแสดงสินค้าในสต็อก สินค้าที่มีราคาต่ำกว่ากำหนด หรือสมาชิกที่ผ่านเกณฑ์บางอย่าง
const availableProducts = products.filter(product => product.stock > 0);
const affordableProducts = products.filter(product => product.price < 100);
const expensiveProducts = products.filter(product => product.price >= 100);
console.log('สินค้าที่มีในสต็อก:', availableProducts);
console.log('สินค้าราคาต่ำกว่า 100:', affordableProducts);
console.log('สินค้าราคา 100 ขึ้นไป:', expensiveProducts);
เงื่อนไข `product.stock > 0` จะตัดปากกาออก เพราะสินค้ามีจำนวนคงเหลือเป็นศูนย์ ส่วนเงื่อนไข `product.price < 100` จะเลือกสมุด ปากกา และดินสอออกมาเป็น Array ใหม่
4. reduce() รวมข้อมูลให้เหลือผลลัพธ์เดียว
`reduce()` ใช้ประมวลผลสมาชิกใน Array ทีละตัว โดยส่งต่อค่าที่สะสมจากรอบก่อนหน้าไปยังรอบถัดไป จึงเหมาะกับการหาผลรวม ยอดขาย จำนวนสินค้า หรือการจัดกลุ่มข้อมูล
const totalStock = products.reduce((total, product) => {
return total + product.stock;
}, 0);
const totalValue = products.reduce((total, product) => {
return total + product.price * product.stock;
}, 0);
console.log('จำนวนสินค้ารวม:', totalStock);
console.log('มูลค่าสินค้าคงคลัง:', totalValue);
ค่า `0` ที่อยู่ท้าย `reduce()` คือค่าเริ่มต้นของตัวสะสม `total` ในตัวอย่างแรก เรานำจำนวนคงเหลือของสินค้าทุกชนิดมาบวกกัน ส่วนตัวอย่างที่สองคำนวณมูลค่าคงคลังด้วยสูตรราคาต่อชิ้นคูณจำนวนคงเหลือ แล้วบวกยอดของทุกสินค้าเข้าด้วยกัน
อย่าลืมค่าเริ่มต้นการระบุค่าเริ่มต้น เช่น `0` ช่วยให้ `reduce()` ทำงานกับ Array ว่างได้อย่างปลอดภัยในกรณีที่ผลลัพธ์ควรเป็นตัวเลข หากไม่ระบุค่าเริ่มต้นและ Array ว่าง จะเกิด TypeError
5. find() ค้นหาสมาชิกตัวแรกที่ตรงเงื่อนไข
`find()` เหมาะเมื่อเราต้องการสมาชิกเพียงรายการแรกที่ตรงกับเงื่อนไข เช่น ค้นหาสินค้าด้วยรหัสสินค้า หรือค้นหาผู้ใช้ด้วยอีเมล หากไม่พบสมาชิกที่ตรงเงื่อนไข ผลลัพธ์จะเป็น `undefined`
const product = products.find(item => item.id === 3);
const missingProduct = products.find(item => item.id === 99);
console.log(product);
console.log(missingProduct);
if (product) {
console.log('สินค้าที่ค้นพบ:', product.name);
} else {
console.log('ไม่พบสินค้า');
}
การใช้ `find()` ไม่ได้คืน Array แต่คืน Object ของสมาชิกตัวแรกที่ตรงเงื่อนไข หรือคืน `undefined` หากไม่มีรายการที่ตรงกัน หากต้องการสมาชิกที่ตรงเงื่อนไขทั้งหมด ให้ใช้ `filter()` แทน
6. some() และ every() ตรวจสอบเงื่อนไข
`some()` ตอบคำถามว่า มีสมาชิกอย่างน้อยหนึ่งรายการที่ผ่านเงื่อนไขหรือไม่ ส่วน `every()` ตอบว่า สมาชิกทุกตัวผ่านเงื่อนไขหรือไม่ ทั้งสอง Method คืนค่าเป็น Boolean คือ `true` หรือ `false`
const hasOutOfStock = products.some(product => product.stock === 0);
const allHavePositivePrice = products.every(product => product.price > 0);
const allInStock = products.every(product => product.stock > 0);
console.log('มีสินค้าหมดสต็อกหรือไม่:', hasOutOfStock);
console.log('สินค้าทุกชิ้นมีราคามากกว่าศูนย์หรือไม่:', allHavePositivePrice);
console.log('สินค้าทุกชิ้นมีสต็อกหรือไม่:', allInStock);
จากข้อมูลตัวอย่าง `hasOutOfStock` จะเป็น `true` เพราะมีปากกาที่สต็อกเป็นศูนย์ และ `allHavePositivePrice` จะเป็น `true` เพราะราคาของทุกชิ้นมากกว่าศูนย์ แต่ `allInStock` จะเป็น `false` เพราะมีสินค้าอย่างน้อยหนึ่งรายการที่หมดสต็อก
7. เปรียบเทียบ Array Methods ที่ใช้บ่อย
| Method | หน้าที่ | ชนิดผลลัพธ์ | ตัวอย่างงาน |
|---|---|---|---|
| map() | แปลงสมาชิกทุกตัว | Array ใหม่ | สร้างรายการชื่อสินค้า |
| filter() | เลือกสมาชิกที่ผ่านเงื่อนไข | Array ใหม่ | แสดงเฉพาะสินค้าที่มีสต็อก |
| reduce() | สะสมหรือรวมข้อมูล | ค่าที่สะสมได้ | คำนวณยอดรวม |
| find() | ค้นหาสมาชิกตัวแรกที่ตรงเงื่อนไข | สมาชิกหรือ undefined | ค้นหาสินค้าตามรหัส |
| some() | ตรวจว่ามีสมาชิกอย่างน้อยหนึ่งตัวผ่านเงื่อนไขหรือไม่ | Boolean | ตรวจว่ามีสินค้าหมดสต็อกหรือไม่ |
| every() | ตรวจว่าสมาชิกทุกตัวผ่านเงื่อนไขหรือไม่ | Boolean | ตรวจว่าราคาสินค้าทุกชิ้นถูกต้องหรือไม่ |
8. เชื่อม Methods เพื่อทำงานหลายขั้นตอน
Array Methods สามารถต่อกันเป็นลำดับได้ เช่น กรองเฉพาะสินค้าที่มีสต็อก จากนั้นคำนวณราคาหลังหักส่วนลด แล้วดึงเฉพาะชื่อและราคาสำหรับแสดงผล วิธีนี้เรียกว่า Method Chaining
const saleList = products
.filter(product => product.stock > 0)
.map(product => ({
name: product.name,
salePrice: Number((product.price * 0.9).toFixed(2))
}))
.filter(product => product.salePrice >= 10);
console.log('รายการสินค้าลดราคา:', saleList);
ลำดับการทำงานมีความสำคัญ: ขั้นแรกคัดสินค้าที่มีสต็อก ขั้นที่สองสร้าง Object ใหม่ซึ่งมีชื่อและราคาลดแล้ว และขั้นสุดท้ายคัดรายการที่ราคาหลังลดตั้งแต่ 10 หน่วยขึ้นไป การต่อ Method ช่วยให้เห็นกระบวนการแปลงข้อมูลได้ต่อเนื่อง แต่ควรแบ่งเป็นตัวแปรย่อยหากเงื่อนไขซับซ้อนจนอ่านยาก
9. Array Methods แก้ไข Array ต้นฉบับหรือไม่
`map()`, `filter()`, `slice()` และ Methods ที่ใช้ค้นหาอย่าง `find()` โดยทั่วไปไม่แก้ไขโครงสร้างของ Array ต้นฉบับ แต่คืนผลลัพธ์หรือค่าที่ค้นพบกลับมา อย่างไรก็ตาม หากสมาชิกเป็น Object แล้วเราแก้คุณสมบัติของ Object ที่อ้างอิงอยู่ ก็อาจกระทบข้อมูลเดิมได้ เพราะการสร้าง Array ใหม่ไม่ได้หมายความว่า Object ภายในถูกคัดลอกแบบลึกเสมอไป
const originalNumbers = [1, 2, 3];
const doubledNumbers = originalNumbers.map(number => number * 2);
console.log(originalNumbers);
console.log(doubledNumbers);
const originalProduct = products[0];
const changedProducts = products.map(product =>
product.id === 1
? { ...product, price: 99 }
: product
);
console.log('ราคาเดิม:', originalProduct.price);
console.log('ราคาใหม่:', changedProducts.find(product => product.id === 1).price);
ตัวอย่างใช้ Spread Syntax (`...product`) เพื่อสร้าง Object ใหม่สำหรับสินค้าที่ต้องการเปลี่ยนราคา วิธีนี้ช่วยหลีกเลี่ยงการแก้ไข Object ต้นฉบับโดยตรง และทำให้ติดตามการเปลี่ยนแปลงของข้อมูลได้ง่ายขึ้น
ข้อควรระวังเรื่อง Objectการใช้ `map()` หรือ `filter()` สร้าง Array ใหม่ แต่ถ้าเก็บ Object เดิมไว้ในผลลัพธ์ สมาชิกเหล่านั้นอาจยังอ้างอิง Object ตัวเดิมอยู่ ควรสร้าง Object ใหม่เมื่อจำเป็นต้องแก้ไขข้อมูลโดยไม่กระทบต้นฉบับ
10. ฝึกคิดจากสถานการณ์จริง
ลองพิจารณาระบบร้านค้าที่ต้องการข้อมูลสามอย่าง ได้แก่ รายการสินค้าที่ราคาไม่เกิน 100 และมีสต็อก ยอดมูลค่าสินค้าคงคลังรวม และตรวจว่ามีสินค้าราคาเกิน 400 หรือไม่ คุณสามารถเลือก Method ที่เหมาะสมกับผลลัพธ์แต่ละประเภทก่อนลงมือเขียนโค้ด
const lowCostInStock = products.filter(product =>
product.price <= 100 && product.stock > 0
);
const inventoryValue = products.reduce((total, product) =>
total + product.price * product.stock,
0
);
const hasPremiumProduct = products.some(product => product.price > 400);
console.log('สินค้าราคาไม่เกิน 100 ที่มีสต็อก:', lowCostInStock);
console.log('มูลค่าสินค้าคงคลังรวม:', inventoryValue);
console.log('มีสินค้าราคาเกิน 400 หรือไม่:', hasPremiumProduct);
11. แบบฝึกหัดท้ายบท
ลองเขียนโค้ดด้วยตนเองก่อนดูเฉลย โดยใช้ Array `scores` ซึ่งเก็บคะแนนของนักเรียนแต่ละคน
const scores = [58, 82, 45, 96, 71, 60];
// 1. สร้าง Array ใหม่ที่เพิ่มคะแนนทุกคนอีก 5 คะแนน
// 2. เลือกคะแนนที่ตั้งแต่ 60 คะแนนขึ้นไป
// 3. หาผลรวมคะแนนทั้งหมด
// 4. ค้นหาคะแนนตัวแรกที่มากกว่า 90
// 5. ตรวจว่ามีคะแนนต่ำกว่า 50 อย่างน้อยหนึ่งรายการหรือไม่
เฉลยแบบฝึกหัด
1. `const bonusScores = scores.map(score => score + 5);` สร้างคะแนนใหม่โดยบวก 5 ให้ทุกคน
2. `const passingScores = scores.filter(score => score >= 60);` เลือกคะแนนตั้งแต่ 60 ขึ้นไป
3. `const totalScore = scores.reduce((total, score) => total + score, 0);` รวมคะแนนทุกคน
4. `const firstHighScore = scores.find(score => score > 90);` ค้นหาคะแนนตัวแรกที่มากกว่า 90
5. `const hasLowScore = scores.some(score => score < 50);` ตรวจว่ามีคะแนนต่ำกว่า 50 หรือไม่
12. สรุปและเตรียมพร้อมสำหรับตอนถัดไป
หัวใจของ Array Methods คือการเลือกเครื่องมือให้ตรงกับผลลัพธ์ที่ต้องการ: `map()` สำหรับแปลงข้อมูล, `filter()` สำหรับคัดกรอง, `reduce()` สำหรับสะสมผล, `find()` สำหรับค้นหาสมาชิกตัวแรก, `some()` สำหรับตรวจว่ามีอย่างน้อยหนึ่งรายการผ่านเงื่อนไข และ `every()` สำหรับตรวจว่าทุกรายการผ่านเงื่อนไข
ภารกิจหลังเรียนเลือกข้อมูลที่คุณใช้อยู่จริง เช่น รายการสินค้า คะแนน หรือรายชื่อสมาชิก แล้วสร้างตัวอย่างที่ใช้ `filter()` ร่วมกับ `map()` และอีกตัวอย่างที่ใช้ `reduce()` เพื่อคำนวณผลรวม พร้อมตรวจสอบว่าโค้ดไม่แก้ไขข้อมูลต้นฉบับโดยไม่ตั้งใจ
โค้ดที่ดีไม่ใช่แค่ทำงานได้ แต่ยังทำให้ผู้อ่านเข้าใจได้ทันทีว่ากำลังแปลง คัดกรอง หรือสรุปข้อมูลอะไร
ตอนถัดไป: **การเรียงลำดับและการจัดการข้อมูลใน JavaScript** เราจะเรียนรู้ `sort()`, `toSorted()`, `reverse()` และวิธีเรียงข้อมูลตัวเลขกับ Object อย่างถูกต้อง พร้อมทำความเข้าใจว่าบาง Method เปลี่ยน Array ต้นฉบับหรือไม่
อ่านจนถึงระยะ 500px ก่อนท้ายหน้าและอยู่ในหน้านี้อย่างน้อย 5 นาที เพื่อรับ EXP วันละครั้ง
⏳ กำลังจับเวลาอ่าน...