เรียงลำดับข้อมูลใน Python: sort(), sorted() และฟังก์ชัน key
บทเรียนนี้แปลจากต้นฉบับภาษาอังกฤษRead in EnglishLeer en españolЧитать на русскомLer em portuguêsแจ้งข้อผิดพลาดในการแปล
ลองนึกถึงชั้นหนังสือที่บ้าน หนังสือวางกระจัดกระจายไปหมด บางเล่มเรียงตามชื่อ บางเล่มเรียงตามสี บางเล่มถูกยัดไว้แบบตะแคง จะหาอะไรสักเล่มก็ใช้เวลานานมาก
ทีนี้ลองนึกภาพว่าจัดชั้นหนังสือนั้นตามลำดับตัวอักษร ทันใดนั้นการหาหนังสือเล่มไหนก็ใช้เวลาแค่ไม่กี่วินาที นี่คือพลังของการเรียงลำดับ (sorting) ที่เปลี่ยนความวุ่นวายให้เป็นระเบียบ
Python มีเครื่องมือหลักสองอย่างสำหรับการเรียงลำดับ คือเมธอด sort() และฟังก์ชัน sorted() ทั้งสองดูคล้ายกันแต่ทำงานต่างกัน ในบทเรียนนี้ คุณจะได้เรียนรู้ว่าควรใช้แต่ละตัวเมื่อไร วิธีเรียงแบบย้อนกลับ และวิธีเรียงตามกฎที่กำหนดเองด้วยฟังก์ชัน key
sort() กับ sorted() ต่างกันอย่างไร
นี่คือความแตกต่างที่สำคัญที่สุดของการเรียงลำดับใน Python เมธอด sort() เปลี่ยนลิสต์ต้นฉบับโดยตรง ส่วนฟังก์ชัน sorted() สร้างลิสต์ใหม่ที่เรียงแล้ว และไม่แตะต้องลิสต์ต้นฉบับเลย
ลองคิดแบบนี้: sort() เหมือนการจัดเรียงหนังสือบนชั้นใหม่ ส่วน sorted() เหมือนการทำบัญชีรายชื่อหนังสือที่เรียงแล้ว โดยปล่อยชั้นหนังสือไว้เหมือนเดิมทุกอย่าง
[1, 2, 5, 8, 9]
[1, 2, 5, 8, 9] [5, 2, 8, 1, 9]
names = ['Charlie', 'Alice', 'Bob']
result = names.sort()
print(result) # None!names = ['Charlie', 'Alice', 'Bob']
result = sorted(names)
print(result) # ['Alice', 'Bob', 'Charlie']ยังมีความแตกต่างสำคัญอีกข้อ: sorted() ใช้ได้กับออบเจกต์ที่วนซ้ำได้ (iterable) ทุกชนิด ทั้งสตริง ทูเพิล ดิกชันนารี หรือแม้แต่เจเนอเรเตอร์ (generator) ส่วนเมธอด sort() ใช้ได้กับลิสต์เท่านั้น
['h', 'n', 'o', 'p', 't', 'y'] [1, 1, 3, 4, 5]
วิธีเรียงลำดับแบบย้อนกลับ
ทั้ง sort() และ sorted() รับพารามิเตอร์ reverse ถ้ากำหนดเป็น True จะเรียงจากมากไปน้อยแทนที่จะเรียงจากน้อยไปมาก
[61, 72, 88, 95, 100] [100, 95, 88, 72, 61]
โดยค่าเริ่มต้น สตริงจะเรียงตามลำดับตัวอักษร เมื่อใช้ reverse=True จะเรียงตามลำดับตัวอักษรแบบย้อนกลับ
['apple', 'banana', 'cherry', 'date'] ['date', 'cherry', 'banana', 'apple']
ฟังก์ชัน key ในการเรียงลำดับของ Python คืออะไร
บางครั้งคุณไม่ได้ต้องการเรียงตามค่าของข้อมูลโดยตรง แต่ต้องการเรียงตามคุณสมบัติบางอย่างของแต่ละค่า เช่น เรียงคำตามความยาวแทนที่จะเรียงตามตัวอักษร
พารามิเตอร์ key ให้คุณส่งฟังก์ชันที่แปลงข้อมูลแต่ละตัวก่อนนำไปเปรียบเทียบ Python จะเรียกฟังก์ชันนี้กับข้อมูลทุกตัว แล้วเรียงลำดับตามผลลัพธ์ที่ได้ ข้อมูลต้นฉบับยังเหมือนเดิม มีเพียงลำดับเท่านั้นที่เปลี่ยน
['pie', 'kiwi', 'banana', 'strawberry']
ในตัวอย่างนี้ len คือฟังก์ชัน key Python เรียก len() กับแต่ละคำแล้วใช้ความยาวเหล่านั้นตัดสินลำดับ "pie" มี 3 ตัวอักษร "kiwi" มี 4 ตัวอักษร และต่อไปเรื่อย ๆ
คุณใช้ฟังก์ชันใดก็ได้เป็น key ตัวอย่างที่พบบ่อยคือการเรียงโดยไม่สนตัวพิมพ์ใหญ่เล็กด้วย str.lower
['Bob', 'CHARLIE', 'alice', 'dave'] ['alice', 'Bob', 'CHARLIE', 'dave']
วิธีเรียงลำดับด้วยฟังก์ชัน lambda
ฟังก์ชันในตัว (built-in) เช่น len และ str.lower ช่วยได้แค่ระดับหนึ่ง เมื่อต้องการตรรกะการเรียงลำดับแบบกำหนดเอง ให้ใช้ lambda ซึ่งเป็นฟังก์ชันเล็ก ๆ ไม่มีชื่อ เขียนได้ในบรรทัดเดียว
lambda รับข้อมูลหนึ่งตัวแล้วคืนค่าที่ใช้ในการเรียง รูปแบบการเขียนคือ lambda item: expression
[12, 23, 34, 45, 67, 89]
lambda lambda x: x % 10 รับตัวเลขแต่ละตัวแล้วคืนหลักหน่วยของมัน (เศษจากการหารด้วย 10) จากนั้น Python เรียงตามหลักหน่วยเหล่านั้น: 2, 3, 4, 5, 7, 9
['zebra', 'apple', 'hello', 'mango']
['ant', 'bat', 'cat', 'dog', 'elephant']
วิธีเรียงดิกชันนารีและข้อมูลที่ซับซ้อน
ในโปรแกรมจริง คุณแทบไม่ได้เรียงลิสต์ของตัวเลขธรรมดา ส่วนใหญ่จะเรียงลิสต์ของดิกชันนารี (dictionary) เช่น เรียงนักเรียนตามคะแนน หรือเรียงสินค้าตามราคา
Bob: 85 Diana: 88 Alice: 92 Charlie: 97
Charlie: 97 Alice: 92 Diana: 88 Bob: 85
คุณยังเรียงคีย์หรือค่าของดิกชันนารีเองได้ด้วย โดยค่าเริ่มต้น ฟังก์ชัน sorted() จะวนซ้ำผ่านคีย์ของดิกชันนารี
apple: $0.75 banana: $0.25 cherry: $3.25 date: $1.75 banana: $0.25 apple: $0.75 date: $1.75 cherry: $3.25
การเรียงลำดับแบบเสถียร (stable sorting) คืออะไร และทำไมจึงสำคัญ
Python ใช้อัลกอริทึมการเรียงลำดับที่เรียกว่า Timsort ซึ่งเสถียร (stable) หมายความว่าเมื่อข้อมูลสองตัวมีค่าเท่ากันตามคีย์ที่ใช้เรียง ข้อมูลทั้งสองจะคงลำดับเดิมไว้
เรื่องนี้สำคัญมากในทางปฏิบัติ ถ้าคุณเรียงนักเรียนตามคะแนน และมีนักเรียนสองคนได้คะแนนเท่ากัน ทั้งสองคนจะอยู่ในลำดับเดิมเหมือนก่อนเรียง
Bob: 85 Diana: 85 Alice: 90 Charlie: 90
แบบฝึกหัด
คุณมีลิสต์คะแนนสอบ ให้แสดงคะแนนที่เรียงจากน้อยไปมาก แล้วในบรรทัดถัดไปให้แสดงคะแนนที่เรียงจากมากไปน้อย
ใช้ sorted() เพื่อให้ลิสต์ต้นฉบับไม่เปลี่ยนแปลง
อ่านโค้ดด้านล่างให้ละเอียด โค้ดจะแสดงอะไร อย่าลืมความแตกต่างสำคัญระหว่าง sort() และ sorted()
พิมพ์ผลลัพธ์ให้ตรงทุกตัวอักษร
เรียงลิสต์ของคำตามความยาว (สั้นที่สุดก่อน) ถ้ามีสองคำที่ยาวเท่ากัน ให้คงลำดับเดิมไว้
แสดงลิสต์ที่เรียงแล้ว
โค้ดด้านล่างพยายามเรียงชื่อตามลำดับตัวอักษร แต่ชื่อที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่และตัวพิมพ์เล็กเรียงปนกันผิดลำดับ ให้แก้การเรียงให้เป็นลำดับตัวอักษรที่แท้จริงโดยไม่สนตัวพิมพ์ใหญ่เล็ก
ผลลัพธ์ควรเป็น: ['alice', 'Bob', 'Charlie', 'dave', 'Eve']
เรียงลิสต์นักเรียนตามคะแนนจากมากไปน้อย แล้วแสดงชื่อและคะแนนของนักเรียนแต่ละคนทีละบรรทัดในรูปแบบ: Name: Grade
เรียงลิสต์สินค้าตามหมวดหมู่ก่อน (ตามลำดับตัวอักษร) แล้วเรียงตามราคา (ถูกที่สุดก่อน) ภายในแต่ละหมวดหมู่
แสดงสินค้าแต่ละรายการทีละบรรทัดในรูปแบบ: category: name - $price