เทคนิคลัด JavaScript ที่คุณควรรู้! (ตอนที่ 1)

Nuttavut Thongjor

กฎเกณฑ์บนโลกนี้บางอย่างก็มีสูตรตายตัวแบบสูตรคำนวน ส.ส. ปาร์ตี้ลิสต์ ที่แม้จะมีสูตรเดียวแต่คำนวณนานประหนึ่งแก้อินทิเกรตสามชั้น

สำหรับบางสิ่งก็ไม่มีกฎตายตัวในการแก้ปัญหา JavaScript ก็เช่นกัน หลากหลายวิธีที่ใช้โค้ดย่อมนำไปสู่การแก้ปัญหาเดียวกันได้ แต่บางครั้งเราก็มีสูตรลัดนะ StackOverflow แอนด์ Copy-Paste ไงละปัดโถ่!

ตัดเศษทิ้งด้วย ~~

สมมติเรามีตัวเลข 21.16 แล้วต้องการตัดเศษทิ้งเหลือ 21 วิธีง่ายสุดคือการใส่ ~~ นำหน้า เช่น ~~21.16 จะได้ค่าเป็น 21 แล้วหละ! แม้จะง่ายแต่ก็ดูขำ ๆ อย่าทะลึ่งไปเขียนซะทุกที่ มันอ่านไม่รู้เรื่อง ไอ้หอยขม!

ตัวเครื่องหมาย ~ เป็นหนึ่งในกลุ่มเครื่องหมายที่เราเรียกว่า Bitwise Operator โดยเงื่อนไขของการใช้ ~~ จะต้องเป็นตัวเลขที่อยู่ระหว่าง (231)-(2^{31}) และ 23112^{31} - 1 ไม่เช่นนั้นจะเกิด overflow และให้ผลลัพธ์ที่ไม่ถูกต้อง

วิธีทางเลือกที่ดีกว่าคือการใช้ฟังก์ชัน Math.trunc เช่น Math.truc(21.16) ที่จะได้ผลลัพธ์เป็น 21 เช่นกัน

กำจัดตัวซ้ำจากอาร์เรย์ด้วย Set

สมมติเรามีอาร์เรย์ที่ของข้างในมีค่าซ้ำกันอยู่ เช่น [1, 1, 2, 3, 2, 4, 5] จะกำจัดให้เหลือเฉพาะค่าไม่ซ้ำแบบนี้ [1, 2, 3, 4, 5] ได้อย่างไร?

ยาวไปไม่อ่าน (TL;DR)

สมมติอาร์เรย์ที่มีค่าซ้ำอยู่ในตัวแปรชื่อ arr ให้เราใช้สูตรนี้คือจบ

JavaScript
1;[...new Set(arr)]

เช่น ต้องการลบค่าซ้ำจาก [1, 1, 2, 3, 2, 4, 5] สามารถทำได้ดังนี้

JavaScript
1;[...new Set([1, 1, 2, 3, 2, 4, 5])] // [1, 2, 3, 4, 5]

คำอธิบาย

ในทางคณิตศาสตร์ เซต หมายถึงกลุ่มของสมาชิกที่สามารถนิยามสมาชิกได้อย่างชัดเจน โดยสมาชิกภายในเซตใด ๆ ย่อมไม่ซ้ำกัน

เพราะความที่เซตนั้นมีสมาชิกไม่ซ้ำ เราจึงจัดการโยนอาร์เรย์เข้าไปในคอนสตรัคเตอร์ของเซต เพื่อกำจัดค่าซ้ำออกไป จากนั้นจึงทำการกระจายออกด้วย spread operator เพื่อให้กลับมาเป็นอาร์เรย์อีกครั้ง

range อย่างง่ายด้วย Array

โจทย์ข้อนี้เราจะลองสร้างฟังก์ชัน range กัน เพื่อผลิตอาร์เรย์ของตัวเลขจากค่าหนึ่งไปสู่ค่าหนึ่ง เช่น range(1, 5) ให้ผลลัพธ์เป็น [1, 2, 3, 4, 5]

ยาวไปไม่อ่าน (TL;DR)

JavaScript
1function range(from, to) {
2 return Array.from({ length: to - from + 1 }, (_v, i) => i + from)
3}
4
5// range(1, 5) => [1, 2, 3, 4, 5]

คำอธิบาย

เรามาเริ่มสูตรลัพธ์ในการสร้างอาร์เรย์จากเลข 0 ถึง n เอาไปสองสูตร ดังนี้

JavaScript
1// สูตรแรก
2;[...Array(n).keys()]
3
4// สูตรที่สอง
5Array.from({ length: n }, (_v, i) => i)

ตัวอย่างเช่น ต้องการสร้างอาร์เรย์ [0, 1, 2, 3, 4] ที่เริ่มต้นจาก 0 และมีทั้งหมด 5 จำนวน จากทั้งสองสูตรสามารถแก้ปัญหาข้างต้นได้ ดังนี้

JavaScript
1// สูตรแรก
2;[...Array(5).keys()]
3
4// สูตรสอง
5Array.from({ length: 5 }, (_v, i) => i)

คราวนี้เราจะขยายความสามารถจากเดิมที่เลขเริ่มจาก 0 เป็นเริ่มจากค่า from จนถึงค่า to จากสองสูตรข้างต้นผนวกความสามารถใหม่เข้าไปได้เป็น

JavaScript
1// สูตรแรก
2;[...Array(to - from + 1).keys()].map((v) => v + x)
3
4// สูตรสอง
5Array.from({ length: to - from + 1 }, (_v, i) => i + from)

ตัวอย่างเช่น ต้องการสร้างอาร์เรย์จากเลข 10 ถึง 20 สามารถใช้สองสูตรได้ ดังนี้

JavaScript
1// 11 มาจาก 20 - 11 + 1
2;[...Array(11).keys()].map((v) => v + 10)
3
4// หรือ
5// Array.from({ length: 11 }, (_v, i) => i + 10)

กลับมาที่โจทย์ของเราที่ต้องการสร้างฟังก์ชัน range ให้ระบุค่าเริ่มต้นและค่าสิ้นสุดได้ อาศัยความสัมพันธ์จากสูตรที่สองข้างต้น จึงเกิดเป็นฟังก์ชัน range หน้าตาแบบนี้

JavaScript
1function range(from, to) {
2 return Array.from({ length: to - from + 1 }, (_v, i) => i + from)
3}
4
5// range(1, 5) => [1, 2, 3, 4, 5]

เอาหละถึงเวลาอธิบายความหมายแล้ว ในที่นี้จะอธิบายความหมายเฉพาะสูตรที่สองนะครับ

สำหรับ Array เมธอด from สามารถใช้เพื่อสร้างอาร์เรย์ใหม่ได้ โดยค่าแรกที่ส่งไปนั้นจะต้องเป็นออบเจ็กต์ประเภท array-like / iterable

ความที่อาร์เรย์ก็เป็นออบเจ็กต์ เราจึงสามารถเสกสร้างออบเจ็กต์ให้เหมือนอาร์เรย์ได้ เช่น ['A', 'B', 'C'] นั้นเป็นอาร์เรย์ความยาวสามช่อง ช่องที่ 0 หรือ [0] มีค่าเป็น A ช่องที่ [1] มีค่าเป็น B และช่องสุดท้ายคือ [2] มีค่าเป็น C เมื่อเราแปลงความสัมพันธ์นี้เป็นออบเจ็กต์จึงได้ว่า ['A', 'B', 'C'] คล้ายกับออบเจ็กต์นี้

JavaScript
1{
2 0: 'A',
3 1: 'B',
4 2: 'C'
5 length: 3
6}

และนี่หละคือสื่งที่เราเรียกว่าเป็นออบเจ็กต์ประเภท array-like

การที่เราใช้ฟังก์ชัน from ด้วยการระบุ length เข้าไปจึงเป็นการจำลอง array-like ที่มีค่าจำนวนตามที่ระบุ และเหตุที่เราไม่ได้แจกแจงว่าแต่ละช่องมีค่าเป็นอะไร ทุกช่องจึงมีค่าเป็น undefined แทน

JavaScript
1Array.from({ length: 3 }) // [undefined, undefined, undefined]

ค่าที่สองของ from เราสามารถส่ง map function อันเป็นฟังก์ชันที่จะถูกเรียกทุกครั้งที่สร้างค่าแต่ละช่องในอาร์เรย์ ฟังก์ชันนี้จะรับค่าของแต่ละช่องเข้ามา เช่นจากตัวอย่างข้างต้นค่าของแต่ละช่องจะเป็น undefined นั่นเอง

ตามธรรมชาติของฟังก์ชันที่ชื่อว่า map เราใช้เพื่อแปลงค่าของอาร์เรย์ในแต่ละช่องให้เป็นอีกค่าหนึ่ง แต่เนื่องจากทุกช่องของเราเป็น undefined เราจึงต้องอาศัยพารามิเตอร์ตัวที่สองของ map คือค่า index มาช่วย

JavaScript
1// _v เป็นค่าแต่ละค่าในแต่ละช่อง นั่นคือ undefined
2// i คือค่า index มีค่าเป็น 0, 1, 2 ตามลำดับ
3Array.from({ length: 3 }, (_v, i) => i) // [0, 1, 2]

และนี่คือหลักการณ์ที่อยู่เบื้องหลังการสร้างฟังก์ชัน range ของเราครับ

แกะ Query String ด้วย URLSearchParams

หากเรามี Query String ใน URL ของเราดังนี้ '?search=prayut&limit=44' เมื่อเราต้องการทราบว่า search นั้นมีค่าเป็นอะไรจะสามารถแกะค่าข้อมูลนี้ออกมาได้อย่างไร?

ถ้าเราใช้งาน lib อย่าง query-string ชีวิตก็จะง่ายขึ้น นั่นเพราะเราสามารถดึงแต่ละค่าที่ต้องการออกมาได้อย่างง่ายได้ แต่หากเราไม่ได้แคร์เบราเซอร์เจ้าปัญหาอย่าง IE ทางเลือกของเราก็มีอีกทางคือการใช้ URLSearchParams

URLSearchParams มีคอนสตรัคเตอร์ให้เราส่งข้อความของ query string ได้ ออบเจ็กต์จาก URLSearchParams จะมีเมธอดต่าง ๆ ให้เราสามารถจัดการเกี่ยวกับก้อน query string นี้ เช่น เมธอด get ที่ใช้สำหรับหาค่าของพารามิเตอร์ที่เราระบุ

JavaScript
1const queryString = '?search=prayut&limit=44'
2const params = new URLSearchParams(queryString)
3
4params.get('search') // prayut

เจอชื่อนี้ ตัดจบเลยแล้วกัน ตัวใครตัวมันน้อ~

สารบัญ

สารบัญ

  • ตัดเศษทิ้งด้วย ~~
  • กำจัดตัวซ้ำจากอาร์เรย์ด้วย Set
  • range อย่างง่ายด้วย Array
  • แกะ Query String ด้วย URLSearchParams