เมธอดของแถวลำดับที่เพิ่มมาใน ES5
จาวาสคริปต์ใน ES5
นั้นได้เพิ่มเมธอดสำหรับทำการวนซ้ำเพื่อไล่จัดการสมาชิกต่างๆในแถวลำดับมาหลายตัว
เดิมทีใน ES3 นั้นปกติจะใช้ while หรือ for เพื่อทำการวนซ้ำ ดังที่เขียนถึงใน
บทที่ ๑๑
แต่เมื่อใช้เมธอดที่เพิ่มมาใน ES5 จะทำให้สะดวกขึ้นมาก
ได้แก่
.forEach(f(x)) |
ไล่ทำฟังก์ชัน f ซ้ำกับสมาชิกในแถวลำดับทีละตัว |
.map(f(x)) |
ไล่ทำฟังก์ชัน f ซ้ำกับสมาชิกในแถวลำดับทีละตัว
แล้วสร้างเป็นแถวลำดับของค่าที่ฟังก์ชัน f คืนมา
|
.filter(f(x)) |
ไล่ทำฟังก์ชัน f ซ้ำกับสมาชิกในแถวลำดับทีละตัว
แล้วคัดเหลือเฉพาะที่คืนค่า true
|
.every(f(x)) |
ไล่ทำฟังก์ชัน f ซ้ำกับสมาชิกในแถวลำดับทีละตัว แล้วดูค่าที่คืนมา
ถ้าทุกตัวเป็น true ก็ได้ true ถ้ามี false แม้แต่ตัวเดียวก็จะได้ false
|
.some(f(x)) |
ไล่ทำฟังก์ชัน f ซ้ำกับสมาชิกในแถวลำดับทีละตัว แล้วดูค่าที่คืนมา
ถ้าทุกตัวเป็น false ก็ได้ false ถ้ามี true แม้แต่ตัวเดียวก็จะได้ true
|
.reduce(f(a,b)) |
ไล่ทำฟังก์ชัน f ซ้ำกับสมาชิกในแถวลำดับทีละตัว
ค่าที่คืนมาจะแทนในพารามิเตอร์ตัวแรกของรอบถัดไป
สุดท้ายจะได้ค่าที่คืนจากรอบสุดท้าย
|
.reduceRight(f(a,b)) |
เหมือน .redece แต่ไล่จากตัวสุดท้ายมา |
เมธอด forEach
วิธีใช้
แถวลำดับ.forEach(ฟังก์ชันที่ต้องการให้จัดการกับสมาชิกทุกตัว)
ตัวอย่าง
var ahan = ["ผัดไทย", "ราดหน้า", "แกงส้ม", "โจ๊กคนอร์"];
var saikhai = function(x) {
raikan_ahan += x + "ใส่ไข่\n";
};
var raikan_ahan = "";
ahan.forEach(saikhai);
alert(raikan_ahan);
ได้
ผัดไทยใส่ไข่
ราดหน้าใส่ไข่
แกงส้มใส่ไข่
โจ๊กคนอร์ใส่ไข่
แต่โดยทั่วไปแล้ววิธีการที่ใช้บ่อยก็คือ
สร้างฟังก์ชันขึ้นมาเดี๋ยวนั้นภายในวงเล็บหลัง forEach เลย คือ
แถวลำดับ.forEach(function(พารามิเตอร์) {
สิ่งที่จะทำซ้ำกับทุกตัว
});
เช่นตัวอย่างที่แล้วเขียนใหม่ให้ได้ผลเหมือนเดิมเป็นแบบนี้
var ahan = ["ผัดไทย", "ราดหน้า", "แกงส้ม", "โจ๊กคนอร์"];
var raikan_ahan = "";
ahan.forEach(function(x) {
raikan_ahan += x + "ใส่ไข่\n";
});
alert(raikan_ahan);
แบบนี้ก็ไม่ต้องไปสร้างฟังก์ชันขึ้นมาใหม่ก่อน
ถ้ายังไงก็จะใช้แค่ตรงนี้แล้วทิ้งไปเลยอยู่แล้ว
ข้อเสียคือเขียนแบบนี้แล้วทำให้วงเล็บซ้อนกัน ดูแล้วเข้าใจยากสำหรับมือใหม่
สาเหตุที่ทำให้จาวาสคริปต์ดูเป็นภาษาที่ไม่สวยงาม
มองแล้วยุ่งเหยิงก็มาจากตรงนี้ด้วย
แต่คนที่ใช้จาวาสคริปต์ทั่วไปจะเขียนแบบนี้กันจนชินเหมือนกับไม่มองว่าเป็นปัญหา
ดังนั้นจึงต้องทำความคุ้นเคยกับการเขียนแบบนี้เอาไว้ด้วย
เมื่อใช้ .forEach สิ่งที่ถูกส่งเข้าไปในฟังก์ชันนั้นนอกจากข้อมูลในแถวลำดับแล้ว
ยังมีเลขลำดับด้วย โดยจะไปแทนในพารามิเตอร์ตัวที่ ๒ ถ้าใส่ลงไปด้วย
สามารถใช้เลขลำดับได้
var khrueangduem = ["โอวัลติน", "น้ำเก๊กฮวย", "โค้ก", "ชาอูหลง"];
var raikan = "";
khrueangduem.forEach(function(x, i) {
raikan += i + ". " + x + "\n";
});
alert(raikan);
ได้
0. โอวัลติน
1. น้ำเก๊กฮวย
2. โค้ก
3. ชาอูหลง
นอกจากนี้หากใส่พารามิเตอร์ตัวที่ ๓ พารามิเตอร์นั้นจะแทนตัวแถวลำดับทั้งหมดเอง
เช่น
var chue = ["ฟลุค", "โช้ค", "ท็อป"];
var s = "";
chue.forEach(function(x, i, ar) {
s += x + "ได้ที่ " + (1 + i) + " ในกลุ่ม {" + ar + "}\n";
});
alert(s);
ได้
ฟลุคได้ที่ 1 ในกลุ่ม {ฟลุค,โช้ค,ท็อป}
โช้คได้ที่ 2 ในกลุ่ม {ฟลุค,โช้ค,ท็อป}
ท็อปได้ที่ 3 ในกลุ่ม {ฟลุค,โช้ค,ท็อป}
ความเปลี่ยนแปลงของข้อมูลในแถวลำดับที่ใช้วนซ้ำ
ข้อมูลในแถวลำดับที่นำมาเข้า .forEach
นั้นแค่ถูกใส่เป็นอาร์กิวเมนต์ให้มาแทนในพารามิเตอร์ของฟังก์ชัน
ดังนั้นต่อให้มีการแทนค่าใหม่เข้าไปในตัวแปรที่เป็นพารามิเตอร์นั้นก็ไม่ได้เป็นการแก้ค่าแต่อย่างใด
var ar = [1, 2, 3];
ar.forEach(function(x) {
x = x + 10;
});
alert(ar); // ได้ 1,2,3
จะเห็นว่าค่าในแถวลำดับ ar ไม่ได้มีการเปลี่ยนแปลงแม้จะใส่ค่าใหม่ใน .forEach
แต่หากสมาชิกข้างในเป็นออบเจ็กต์ก็จะเกิดการเปลี่ยนแปลงพรอเพอร์ตีข้างในได้
เช่น
var pokemon = [
{ chue: "พาราส", lv: 20 },
{ chue: "คงปัง", lv: 21 },
{ chue: "ดิกดา", lv: 23 }
];
pokemon.forEach(function(x) {
x.lv += 2;
});
alert(pokemon[0].lv); // ได้ 22
alert(pokemon[1].lv); // ได้ 23
alert(pokemon[2].lv); // ได้ 25
คุณสมบัติที่กล่าวมานี้ไม่เพียงแค่ forEach
แต่ฟังก์ชันอื่นที่จะแนะนำในบทนี้เองก็เช่นกัน
เมธอด map
เมธอด map นั้นคล้ายกับ forEach คือจะวนซ้ำไล่ตามสมาชิกในแถวลำดับ
แต่จะต่างกันตรงที่ฟังก์ชันที่ใช้ใน map นั้นจะต้องมีการคืนค่ากลับมา
และค่านั้นจะถูกนำไปสร้างเป็นแถวลำดับใหม่
ตัวอย่างเช่น ใช้
var kanmusu = ["คิริชิมะ", "อากางิ", "คงโงว", "ยูดาจิ"];
var kanmusu_kaini = kanmusu.map(function(x) {
return x + " ไคนิ ";
});
alert(kanmusu_kaini); // ได้ คิริชิมะ ไคนิ ,อากางิ ไคนิ ,คงโงว ไคนิ ,ยูดาจิ ไคนิ
alert(kanmusu); // ได้ คิริชิมะ,อากางิ,คงโงว,ยูดาจิ
map จะคืนค่าที่คืนกลับมาเป็นแถวลำดับใหม่ ส่วนตัวข้อมูลในแถวลำดับเดิมที่ถูกใช้
map นั้นจะไม่ได้มีการเปลี่ยนแปลง
เมธอด filter
เมธอด filter ใช้สำหรับเป็นตัวกรองข้อมูลเพื่อคัดเหลือเฉพาะส่วนที่ต้องการ
วิธีการใช้คล้ายกับ map แต่ฟังก์ชันที่ใช้กับ filter
จะเป็นเงื่อนไขในการเหลือข้อมูลไว้ true หรือ false
ผลที่ได้คือจะคืนค่าเป็นแถวลำดับของสมาชิกตัวที่ให้ค่าเป็น true ส่วนตัวที่ false
จะไม่ได้มาด้วย
ตัวอย่าง
var lek = [2, 5, 6, 9, 10, 11, 19, 28];
var lekkhu = lek.filter(function(x) {
return x % 2 == 0;
});
alert(lekkhu); // ได้ 2,6,10,28
var lekkhi = lek.filter(function(x) {
return x % 2 == 1;
});
alert(lekkhi); // ได้ 5,9,11,19
อีกตัวอย่าง
var pokemon = [
{ chue: "การ์ดี", nak: "19.0" },
{ chue: "เนียวโรโม", nak: "12.4" },
{ chue: "เคซีย์", nak: "19.5" },
{ chue: "วันริกี", nak: "19.5" },
{ chue: "มาดัตสึโบมิ", nak: "4.0" }
];
var pokemon_bao = pokemon.filter(function(x) {
return x.nak < 15;
});
alert(pokemon_bao.length); // ได้ 2
alert(pokemon_bao[0].chue); // ได้ เนียวโรโม
alert(pokemon_bao[1].chue); // ได้ มาดัตสึโบมิ
alert(pokemon_bao[2].chue); // ได้ TypeError: Cannot read property 'chue' of undefined
จากตัวอย่างนี้เป็นการคัดกรองเฉพาะ pokemon ตัวที่มี nak (น้ำหนัก) ต่ำว่า 15
ผลที่ได้จึงเหลือแค่ ๒ ตัว
เมธอด every และ some
เมธอด every จะนำเอาสมาชิกแต่ละตัวในแถวลำดับมาเข้าฟังก์ชันที่ใส่ลงไป
แล้วจะดูว่าทุกตัวล้วนคืนค่า true มาหรือเปล่า ถ้า true ทั้งหมดจึงจะคืนค่า true
แต่ถ้ามีไม่ใช่ true แม้แต่ตัวเดียวก็จะคืนค่า false
ส่วน some นั้นจะคล้ายกัน แต่จะคืนค่า true เมื่อมี true แม้แค่เพียงตัวเดียว
ต้องไม่มี true สักตัวจึงจะได้ false
ตัวอย่าง
var pokemon = [
{ chue: "อีวุย", sung: 0.3, nak: 6.5 },
{ chue: "เชาเวอร์ส", sung: 1.0, nak: 29.0 },
{ chue: "ธันเดอร์ส", sung: 0.8, nak: 24.5 },
{ chue: "บูสเตอร์", sung: 0.9, nak: 25.0 }
];
var f1 = function(x) {
return x.sung > 0.5;
};
alert(pokemon.map(f1)); // ได้ false,true,true,true
alert(pokemon.some(f1)); // ได้ true
alert(pokemon.every(f1)); // ได้ false
alert(pokemon.filter(f1).length); // ได้ 3
var f2 = function(x) {
return x.nak > 30;
};
alert(pokemon.map(f2)); // ได้ false,false,false,false
alert(pokemon.some(f2)); // ได้ false
alert(pokemon.every(f2)); // ได้ false
alert(pokemon.filter(f2).length); // ได้ 0
var f3 = function(x) {
return x.nak < 30;
};
alert(pokemon.map(f3)); // ได้ true,true,true,true
alert(pokemon.some(f3)); // ได้ true
alert(pokemon.every(f3)); // ได้ true
alert(pokemon.filter(f3).length); // ได้ 4
อนึ่ง หากไม่สนเรื่องค่าที่คืนกลับมาแล้ว เมธอดทั้ง every, some, filter, map
ล้วนสามารถทำหน้าที่แทน forEach ได้
เพราะยังไงฟังก์ชันที่ถูกใส่ลงไปก็จะถูกนำไปทำกับสมาชิกทุกตัว เพียงแต่ forEach
จะไม่ได้คืนค่าอะไรออกมาเท่านั้น
เมธอด reduce และ reduceRight
เมธอดสุดท้ายในกลุ่มนี้ ซึ่งมีการทำงานที่ค่อนข้างซับซ้อนกว่า ๕
ตัวที่แนะนำมาข้างต้นสักหน่อย นั่นคือ .reduce
เมธอด reduce จะทำการจัดการกับสมาชิกในแถวลำดับ โดยที่รอบแรกจะทำกับ ๒ ตัวแรก
(ตำแหน่ง 0 กับ 1) โดยจะมาแทนในพารามิเตอร์ตัวแรกและตัวที่ ๒
จากนั้นค่าที่คืนกลับในรอบแรกจะกลายมาเป็นพารามิเตอร์ในการวนซ้ำรอบที่ ๒
โดยที่รอบนี้พารามิเตอร์ตัวที่ ๒ จะเป็นค่าตัวที่ ๓ (ตำแหน่ง 2)
จากนั้นค่าที่คืนกลับจากรอบนี้ก็จะถูกนำไปใช้ในรอบถัดไป และไล่ไปเรื่อยๆ
จนครบจำนวนสมาชิก
สมมุติว่ามีสมาชิก n ตัว ฟังก์ชันจะถูกทำทั้งหมด n-1 ครั้ง โดยครั้งที่ i
จะใช้สมาชิกตัวที่ i มาเป็นพารามิเตอร์ตัวที่ ๒
หากต้องการเลขลำดับให้ใส่เป็นพารามิเตอร์ตัวที่ ๓ ต่อได้
รูปแบบการเขียนเป็นดังนี้
แถวลำดับ.reduce(function(a, b, i) {
return ค่าที่จะแทนใน a ในรอบถัดไป;
});
ในที่นี้ b คือสมาชิกตัวหลังที่ไล่ไปถึง ส่วน i คือลำดับของสมาชิกตัวนั้น ส่วน a
ในรอบแรกจะเป็นสมาชิกตัวแรก (ลำดับที่ 0)
แต่ตั้งแต่รอบถัดไปจะเป็นค่าที่ได้มาจากรอบที่แล้ว
อธิบายเป็นคำพูดแบบนี้อาจเข้าใจได้ยาก
ดูตัวอย่างประกอบน่าจะช่วยให้เข้าใจได้ง่ายขึ้น
ตัวอย่าง
var merazoma = ["เม", "รา", "โซ", "ม่า"];
var s = "";
var phonlap = merazoma.reduce(function(a, b, i) {
s += "รอบที่ " + i + ": ";
s += "a = " + a + ", ";
s += "b = " + b + "\n";
return a + b;
});
alert(s + "สุดท้ายได้: " + phonlap);
ได้
รอบที่ 1: a = เม, b = รา
รอบที่ 2: a = เมรา, b = โซ
รอบที่ 3: a = เมราโซ, b = ม่า
สุดท้ายได้: เมราโซม่า
ในตัวอย่างนี้รอบแรก a เป็นสมาชิกตัวแรก คือ "เม" ส่วน b เป็นตัวถัดมา
คือ "รา" สุดท้ายฟังก์ชันนี้คืนค่า "เมรา" ออกมา
และถูกนำไปแทนเป็น a ในรอบถัดไป
ไล่แบบนี้ไปเรื่อยๆ จนจบแถวลำดับ
หากเขียนใหม่ให้ทำแบบเดียวกันโดยใช้ while จะได้แบบนี้
var merazoma = ["เม", "รา", "โซ", "ม่า"];
var s = "";
var i = 1;
var len = merazoma.length;
var a = merazoma[0];
while (i < len) {
var b = merazoma[i];
s += "รอบที่ " + i + ": ";
s += "a = " + a + ", ";
s += "b = " + b + "\n";
a = a + b;
i++;
}
var phonlap = a;
alert(s + "สุดท้ายได้: " + phonlap);
จะเห็นว่าพอใช้ .reduce แล้วจะเขียนสั้นกว่าเขียน while ตามแบบเดิม
ส่วนเมธอด reduceRight จะคล้ายกับเมธอด reduce แต่ต่างกันตรงที่ทำจากขวามาซ้าย
ไล่จากตัวสุดท้ายก่อน
ตัวอย่าง
var merazoma = ["ม่า", "รา", "กี", "เบ"];
var s = "";
var phonlap = merazoma.reduceRight(function(a, b, i) {
s += "รอบที่ " + i + ": ";
s += "a = " + a + ", ";
s += "b = " + b + "\n";
return a + b;
});
alert(s + "สุดท้ายได้: " + phonlap);
ได้
รอบที่ 2: a = เบ, b = กี
รอบที่ 1: a = เบกี, b = รา
รอบที่ 0: a = เบกีรา, b = ม่า
สุดท้ายได้: เบกีราม่า
เมื่อใช้ .reduceRight เลขลำดับซึ่งแทนในพารามิเตอร์ตัวที่ ๓ นั้นจะนับถอยหลังแทน
โดยเริ่มจาก ความยาว-2 ไล่ไปจนถึง 0
ประยุกต์ใช้ reduce และ reduceRight
ประโยชน์ที่พอจะเห็นใช้งาน .reduce และ .reduceRight กันนั้น เช่น
การรวมสมาชิกทุกตัวในแถวลำดับ
var lek = [3, 5, 9, 12, 18];
var ruam = lek.reduce(function(a, b) {
return a + b;
});
alert(ruam); // ได้ 47
การหาค่าสูงสุดหรือต่ำสุดในแถวลำดับ
var lek = [66, 32, 11, 71, 43, 25, 38];
var maksut = lek.reduce(function(a, b) {
if (a > b) return a;
else return b;
});
alert(maksut); // ได้ 71
var noisut = lek.reduce(function(a, b) {
if (a < b) return a;
else return b;
});
alert(noisut); // ได้ 11
หาว่าตัวไหนยาวที่สุด พร้อมทั้งดูค่าลำดับของตัวนั้น
var chue = ["นางาโตะ", "มุตสึ", "อิเสะ", "ยุกิกาเซะ", "ฮิวงะ", "คางะ"];
var yaosut = chue.reduce(function(a, b, i) {
if (i == 1) a = { i: 0, chue: a };
b = { i: i, chue: b };
if (a.chue.length > b.chue.length) return a;
else return b;
});
alert("ที่ชื่อยาวสุดคือ " + yaosut.chue + " ลำดับที่ " + yaosut.i); // ได้ ที่ชื่อยาวสุดคือ ยุกิกาเซะ ลำดับที่ 3
จะเห็นว่าฟังก์ชันต่างๆเหล่านี้ช่วยให้การวนซ้ำเพื่อทำอะไรต่างๆในสมาชิกของแถวลำดับนั้นง่ายลง
เขียนสั้นลง หากเทียบกับการใช้ while เพื่อวนซ้ำ
หากใช้เป็นก็สามารถนำไปใช้ทำอะไรต่างๆได้มากมาย