φυβλαςのβλογ
บล็อกของ phyblas



บทที่ ๓๗: การวนซ้ำด้วย for๛of และการใช้แม็ปและเซ็ต
เขียนเมื่อ 2020/02/05 09:22
แก้ไขล่าสุด 2020/02/06 18:20


ในบทนี้จะแนะนำวิธีการวนซ้ำด้วย for๛of พร้อมกับออบเจ็กต์ชนิดใหม่ ๒ ชนิดที่มีลักษณะคล้ายกับแถวลำดับ (array) ที่มีอยู่แต่แรก แต่ว่ามีการใช้งานต่างกันออกไป คือแม็ป (map) และเซ็ต (set) ทั้ง ๒ นี้ถูกเพิ่มเข้ามาใน ES6




การวนซ้ำด้วย for๛of ในแถวลำดับและสายอักขระ

for๛of เป็นคำสั่งที่ใช้กับข้อมูลชนิดแถวลำดับ รวมถึงข้อมูลชนิดอื่นๆที่สามารถไล่เรียงได้ เช่นแม็ปและเซ็ต ซึ่งจะกล่าวถึงต่อไป

หน้าที่ของ for๛of คือเอาไว้ไล่สมาชิกข้างในออกมาทีละตัว

ตัวอย่างการใช้งาน
let s = "";
let rikekoi = ["ยุกิมุระ", "ฮิมุโระ", "คานาเดะ", "อิบาราดะ"];
for (let k of rikekoi){
  s += "[" + k + "]";
}
alert(s); // ได้ [ยุกิมุระ][ฮิมุโระ][คานาเดะ][อิบาราดะ]

ในจาวาสคริปต์ตั้งแต่เดิมก็มีคำสั่งวนซ้ำด้วย for๛in (ดูในบทที่ ๒๓) ซึ่งดูเผินๆแล้วคล้ายกับ for๛of แต่จริงๆต่างกันพอสมควร และอาจทำให้สับสนได้ง่าย

for๛in นั้นเมื่อใช้กับแถวลำดับจะไล่เรียงเลขลำดับของค่าข้างใน ไม่ใช่ตัวค่าข้างใน ถ้าต้องการค่าข้างในก็ต้องใช้ตัวเลขนั้นมาใส่ใน [] อีกที

ตัวอย่างการใช้ for๛in เทียบกับ for๛of ด้านบนเพื่อให้เห็นความแตกต่าง
let s = "";
let rikekoi = ["ชินยะ", "อายาเมะ", "โคโตโนฮะ", "เอนะ"];
for (let i in rikekoi){
    let k = rikekoi[i];
    s += "[" + i + ": " + k + "]";
}
alert(s); // ได้ [0: ชินยะ][1: อายาเมะ][2: โคโตโนฮะ][3: เอนะ]

จะเห็นว่าเมื่อใช้ for๛in จะเป็นการไล่เลขลำดับ 0, 1, 2, ... ถ้าจะเอาข้อมูลข้างในก็ต้องเอาเลขลำดับที่ได้นั้นไปใช้เป็นคีย์อีกที ซึ่งจะดูแล้วยุ่งยาก

ในขณะที่ for๛of นั้นจะไล่เรียงเอาค่าข้างในเลย

for๛of ในจาวาสคริปต์จะเหมือนกับ for๛in ในภาษาไพธอนหรือรูบี ในขณะที่ for๛in ในจาวาสคริปต์จะต่างออกไป ดังนั้นต้องระวังอย่าสับสน

นอกจากนี้ for๛in ยังสามารถใช้กับสายอักขระได้ด้วย เช่น
let sss = "นางาโนะ";
let arr = []
for (let s of sss){
    arr.unshift(s);
}
alert(arr); // ได้ ะ,น,โ,า,ง,า,น



ข้อแตกต่างระหว่างแม็ปกับออบเจ็กต์ธรรมดา

แม็ป (map) เป็นสิ่งที่อาจเปรียบเหมือนแถวลำดับแบบจับคู่ (associative array) ในภาษาอื่นทั่วไป หรือถ้าเทียบเป็นภาษาไพธอนก็คือดิกชันนารี (dictionary) ในภาษาซีคือโครงสร้างข้อมูล (structure)

ในบทที่ ๙ ได้พูดถึงเรื่องการสร้างออบเจ็กต์ในจาวาสคริปต์นั้นก็เปรียบเสมือนแถวลำดับแบบจับคู่ สามารถใช้งานในลักษณะเดียวกันได้เหมือนกัน คือมีคีย์ (key) จับคู๋กับค่า (value)

อย่างไรก็ตาม แม็ป ซึ่งถูกนำเข้ามาใช้ใหม่ใน ES6 นี้ก็ยังมีข้อแตกต่างจากออบเจ็กต์แบบดั้งเดิม ดังนี้
  • ค่าของข้อมูลในออบเจ็กต์ธรรมดานั้นจริงๆก็คือค่าของพรอเพอร์ตี แต่ในแม็ปจะเก็บส่วนของค่าเอาไว้แยกจากพรอเพอร์ตี ช่วยให้ไม่ปะปนกัน ซึ่งบางครั้งก็ทำให้เกิดบั๊กที่ไม่คาดคิดได้เหมือนกัน

  • ในออบเจ็กต์ธรรมดานั้น คีย์ก็คือชื่อพรอเพอร์ตี ซึ่งอาจเป็นได้แค่สายอักขระหรือซิมโบลเท่านั้น (การใช้ซิมโบลดูใน บทที่ ๓๕) หรือถึงแม้จะใช้เป็นตัวเลข แต่เนื้อในก็คือสายอักขระอยู่ดี ในขณะที่แม็ปจะใช้คีย์เป็นข้อมูลขนิดไหนก็ได้

  • แม็ปสามารถหาจำนวนข้อมูลข้างในได้ง่ายกว่า แค่ดูที่พรอเพอร์ตี .size ในขณะที่ออบเจ็กต์ธรรมดาไม่มีวิธีดูโดยตรง อาจต้องใช้ฟังก์ชัน Object.keys() เพื่อไล่เอาคีย์ออกมา แล้วค่อยหา .length

  • ออบเจ็กต์ธรรมดาใช้การวนซ้ำด้วย for๛of ไม่ได้ แต่แม็ปใช้ได้

  • ออบเจ็กต์ธรรมดาใช้การวนซ้ำด้วย for๛in ได้ แต่แม็ปใช้ไม่ได้



การสร้างและใช้แม็ป

แม็ปเป็นออบเจ็กต์ชนิดหนึ่ง วิธีการสร้างต้องใช้ new กับคอนสตรักเตอร์ Map() โดยใส่แถวลำดับที่ใส่แถวลำดับของคีย์และค่า
let mappu = new Map([
  ["เอฮิเมะ", "มัตสึยามะ"],
  ["คางาวะ", "ทากามัตสึ"],
  ["คานางาวะ", "โยโกฮามะ"],
]);

การดูค่าข้างในใช้ .get("คีย์") ไม่ใช่ใช้วงเล็บเหลี่ยม [ ] เหมือนอย่างในออบเจ็กต์ธรรมดา ถ้าใส่คีย์ที่ไม่มีจะได้ undefined
alert(mappu.get("คางาวะ")); // ได้ ทากามัตสึ
alert(mappu.get("ไอจิ")); // ได้ undefined
alert(mappu["คางาวะ"]); // ได้ undifined

สามารถใช้ .set() เพื่อเพิ่มสมาชิกใหม่เข้าไปทีหลังก็ได้
mappu.set("ไอจิ", "นาโงยะ")
alert(mappu.get("ไอจิ")); // ได้ นาโงยะ

สามารถใช้ .forEach(ฟังก์ชัน) เพื่อไล่เรียงค่าออกมาดูทีละตัวได้
let s = "";
mappu.forEach((x) => {
  s += "~" + x + "≈";
})
alert(s); // ได้ ~มัตสึยามะ≈~ทากามัตสึ≈~โยโกฮามะ≈~นาโงยะ≈

ดูว่ามีคีย์ที่ต้องการหรือเปล่าโดยใช้ .has()
alert(mappu.has("ไอจิ")); // ได้ true
alert(mappu.has("กิฟุ")); // ได้ false

ดูจำนวนสมาชิกที่มีอยู่ได้ที่พรอเพอร์ตี .size (ระวัง ไม่ต้องใส่วงเล็บข้างหลังเพราะเป็นพรอเพอร์ตี ไม่ใช่เมธอดเหมืือนอย่างตัวอื่น)
alert(mappu.size) // ได้ 4

ลบสมาชิกได้โดยใช้ .delete("คีย์") หรือถ้าต้องการลบทั้งหมดก็ใช้ .clear()
mappu.delete("คางาวะ")
alert(mappu.size) // ได้ 3
mappu.clear()
alert(mappu.size) // ได้ 0




ข้อควรระวังเกี่ยวกับคีย์ในการใช้แม็ป

แม็ปจะต่างจากออบเจ็กต์ตรงที่ตัวเลขและสายอักขระใช้แทนกันไม่ได้ เช่นลองเทียบดูตามนี้
let obji = { 1.1: "หนึ่งจุดหนึ่ง" };
alert(obji[1.1]); // ได้ หนึ่งจุดหนึ่ง
alert(obji["1.1"]); // ได้ หนึ่งจุดหนึ่ง

let mapi = new Map([[1.1, "หนึ่งจุดหนึ่ง"]]);
alert(mapi.get(1.1)); // ได้ หนึ่งจุดหนึ่ง
alert(mapi.get("1.1")); // ได้ undefined

จะเห็นว่าออบเจ็กต์ธรรมดาใส่ตัวเลขหรือสายอักขระก็ได้ แต่แม็ปต้องใส่เป็นตัวเลขจริงๆเท่านั้น เพราะคีย์ที่ใส่ไปนั้นเป็นตัวเลข แต่ถ้าคีย์ที่ใส่ไปเป็นสายอักขระก็ต้องใส่คีย์เป็นสายอักขระเท่านั้นจึงจะเข้าถึง

และ NaN ก็ใช้เป็นคีย์ได้เช่นกัน และแม้ว่าปกติแล้ว NaN==NaN จะได้ false ก็ตาม แต่ในโลกของแม็ปจะมองว่า NaN ก็คือ NaN เหมือนกัน
let mapo = new Map([[NaN,"หน่านี้ !"]]);
alert(mapo.get(NaN)) // ได้ หน่านี้ !

ในขณะที่ถ้าคีย์เป็นออบเจ็กต์ จะดูว่าเป็นออบเจ็กต์ตัวเดียวกันหรือเปล่า ถ้าเป็นออบเจ็กต์คนละตัวกัน แม้จะหน้าตาเหมือนกันก็ไม่ถือว่าใช้แทนกันได้
let mapa = new Map([[{},"ไรเนี่ย !"]]);
alert(mapa.get({})) // ได้ undefined

let oba = {};
let mape = new Map([[oba,"ไรเนี่ย !"]]);
alert(mape.get(oba)) // ได้ ไรเนี่ย !




การวนซ้ำด้วย for๛of ในแม็ป

แม็ปสามารถใช้ for๛of ได้เช่นเดียวกับแถวลำดับ แต่มีข้อแตกต่างคือแม็ปจะมีทั้งคีย์และค่าออกมาพร้อมกัน จึงต้องการตัวแปร ๒ ตัวมารับ

ตัวอย่างการใช้งาน
let mappa = new Map([
   ["ฮกไกโด", "ซัปโปโระ"],
  ["อิวาเตะ", "โมริโอกะ"],
  ["มิยางิ", "เซนได"],
]);

let s = "";
for (let [k, v] of mappa){
  s += "[" + k + ": " + v + "]";
}
alert(s); // ได้ [ฮกไกโด: ซัปโปโระ][อิวาเตะ: โมริโอกะ][มิยางิ: เซนได]

หากต้องการไล่แค่คีย์ให้ใช้เมธอด .keys() หรือถ้าต้องการแค่ค่าก็ใช้เมธอด .values() นอกจากนี้ยังมีเมธอด entries() ซึ่งจะไล่เรียงค่ากับคีย์ออกมาพร้อมกัน แต่ก็ไม่ต่างอะไรกับการใช้ for๛of โดยตรง
let mappia = new Map([
  ["โอกินาวะ", "นาฮะ"],
  ["ชิมาเนะ", "มัตสึเอะ"],
  ["เฮียวโงะ", "โควเบะ"],
]);

let kk = "";
for (let k of mappia.keys()) {
  kk += "[" + k + "]";
}
alert(kk); // ได้ [โอกินาวะ][ชิมาเนะ][เฮียวโงะ]

let vv = "";
for (let v of mappia.values()) {
  vv += "[" + v + "]";
}
alert(vv); // ได้ [นาฮะ][มัตสึเอะ][โควเบะ]

let kkvv = "";
for (let [k, v] of mappia.entries()) {
  kkvv += "[" + k + ": " + v + "]";
}
alert(kkvv); // ได้ [โอกินาวะ: นาฮะ][ชิมาเนะ: มัตสึเอะ][เฮียวโงะ: โควเบะ]

ที่จริงแล้วเมธอด .keys() .values() .entry() นี้เป็นเมธอดที่จะให้ผลเป็นอิเทอเรเตอร์ที่มาดึงข้อมูลในออบเจ็กต์นั้นไปวนซ้ำ เกี่ยวกับเรื่องอิเทอเรเตอร์เขียนถึงในบทที่ ๓๘




สรุปเมธอดและพรอเพอร์ตีของแม็ป

size จำนวนสมาชิกทั้งหมด
set(คีย์, ค่า) ตั้งค่าให้คีย์นั้น
get(คีย์) เอาค่าที่เก็บอยู่ในคีย์นั้น
has(คีย์) ดูว่ามีคีย์นั้นหรือไม่ (ได้ true, false)
forEach(ฟังก์ชัน) ไล่เอาคีย์มาทำกับฟังก์ชันทีละตัว
keys() ไล่เรียงคีย์ (ใช้กับ for๛of)
values() ไล่เรียงค่า (ใช้กับ for๛of)
entries() ไล่เรียงค่าและคีย์ (ใช้กับ for๛of)
delete(คีย์) ลบคีย์ตัวนั้นออก
clear() ลบสมาชิกทั้งหมด




การสร้างและใช้เซ็ต

เซ็ต (set) คือออบเจ็กต์ที่เก็บค่าหลายๆตัวเรียงต่อกันคล้ายกับแถวลำดับ แต่จะต่างกันตรงที่จะไม่มีสมาชิกซ้ำกันในเซ็ต

การสร้างเซ็ตทำได้โดยใช้คอนสตรักเตอร์ Set() โดยใส่แถวลำดับของสมาชิกที่ต้องการใส่ในเซ็ตลงไป
let setto = new Set(["มาโดกะ", "โฮมุระ", "ซายากะ"]);
alert(setto); // ได้ [object Set]

ดูจำนวนสมาชิกได้ที่พรอเพอร์ตี .size
alert(setto.size); // ได้ 3

เพิ่มตัวใหม่เข้าไปโดยใช้ .add() แต่ถ้าตัวที่เพิ่มเข้าไปมีอยู่แล้วจะไม่เกิดอะไรขึ้น
setto.add("มามิ");
alert(setto.size); // ได้ 4
setto.add("โฮมุระ");
alert(setto.size); // ได้ 4

ดูว่ามีค่าไหนอยู่หรืือเปล่าด้วย .has()
alert(setto.has("เคียวโกะ")); // ได้ false
alert(setto.has("ซายากะ")); // ได้ true

ใช้ .forEach() เพื่อวนซ้ำได้
let ss = "";
setto.forEach((x)=>{
  ss += "{" + x + "}";
});
alert(ss); // ได้ {มาโดกะ}{โฮมุระ}{ซายากะ}{มามิ}

ใช้ for๛of ในการวนซ้ำได้แบบเดียวกับแถวลำดับ โดยอาจใช้ .values() ก็จะได้ผลเหมือนเดิม
let s = "";
for (let x of setto) {
  s += "♫" + x + "♬";
}
alert(s) // ได้ ♫มาโดกะ♬♫โฮมุระ♬♫ซายากะ♬♫มามิ♬

let s = "";
for (let x of setto.values()) {
  s += "♩" + x + "♪";
}
alert(s); // ได้ ♩มาโดกะ♪♩โฮมุระ♪♩ซายากะ♪♩มามิ♪

ลบค่าออกได้โดยใช้ .delete() ถ้าจะลบทั้งหมดใช้ .clear()
setto.delete("มามิ");
alert(setto.size); // ได้ 3
setto.clear();
alert(setto.size); // ได้ 0




สรุปเมธอดและพรอเพอร์ตีของเซ็ต

size จำนวนสมาชิกทั้งหมด
add(ค่า) เพิ่มค่านั้นเข้าไป
has(ค่า) ดูว่ามีค่านั้นหรือไม่ (ได้ true, false)
forEach(ฟังก์ชัน) ไล่เอาค่ามาทำกับฟังก์ชันทีละตัว
values() ไล่เรียงค่า (ใช้กับ for๛of)
delete(ค่า) ลบคีย์ตัวนั้นออก
clear() ลบสมาชิกทั้งหมด






-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> javascript

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
python
-- numpy
-- matplotlib

-- pandas
-- pytorch
maya
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
javascript
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
เรียนภาษาจีน
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
หลักการเขียนทับศัพท์ภาษาจีนกวางตุ้ง
การใช้ unix shell เบื้องต้น ใน linux และ mac
หลักการเขียนทับศัพท์ภาษาจีนกลาง
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
บันทึกการเที่ยวสวีเดน 1-12 พ.ค. 2014
แนะนำองค์การวิจัยและพัฒนาการสำรวจอวกาศญี่ปุ่น (JAXA)
เล่าประสบการณ์ค่ายอบรมวิชาการทางดาราศาสตร์โดยโซวเคนได 10 - 16 พ.ย. 2013
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
บันทึกการเที่ยวญี่ปุ่นครั้งแรกในชีวิต - ทุกอย่างเริ่มต้นที่สนามบินนานาชาติคันไซ
หลักการเขียนทับศัพท์ภาษาญี่ปุ่น
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ
ทำไมถึงอยากมาเรียนต่อนอก
เหตุผลอะไรที่ต้องใช้ภาษาวิบัติ?

บทความแต่ละเดือน

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2019年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2018年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2017年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2016年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文