ข้ามไปยังเนื้อหาหลัก
Editor

เรียงลำดับข้อมูลใน Python: sort(), sorted() และฟังก์ชัน key

ปานกลาง25 นาที6 แบบฝึกหัด85 XP

บทเรียนนี้แปลจากต้นฉบับภาษาอังกฤษRead in EnglishLeer en españolЧитать на русскомLer em portuguêsแจ้งข้อผิดพลาดในการแปล

0/6 แบบฝึกหัด

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

ทีนี้ลองนึกภาพว่าจัดชั้นหนังสือนั้นตามลำดับตัวอักษร ทันใดนั้นการหาหนังสือเล่มไหนก็ใช้เวลาแค่ไม่กี่วินาที นี่คือพลังของการเรียงลำดับ (sorting) ที่เปลี่ยนความวุ่นวายให้เป็นระเบียบ

Python มีเครื่องมือหลักสองอย่างสำหรับการเรียงลำดับ คือเมธอด sort() และฟังก์ชัน sorted() ทั้งสองดูคล้ายกันแต่ทำงานต่างกัน ในบทเรียนนี้ คุณจะได้เรียนรู้ว่าควรใช้แต่ละตัวเมื่อไร วิธีเรียงแบบย้อนกลับ และวิธีเรียงตามกฎที่กำหนดเองด้วยฟังก์ชัน key

sort() กับ sorted() ต่างกันอย่างไร

นี่คือความแตกต่างที่สำคัญที่สุดของการเรียงลำดับใน Python เมธอด sort() เปลี่ยนลิสต์ต้นฉบับโดยตรง ส่วนฟังก์ชัน sorted() สร้างลิสต์ใหม่ที่เรียงแล้ว และไม่แตะต้องลิสต์ต้นฉบับเลย

ลองคิดแบบนี้: sort() เหมือนการจัดเรียงหนังสือบนชั้นใหม่ ส่วน sorted() เหมือนการทำบัญชีรายชื่อหนังสือที่เรียงแล้ว โดยปล่อยชั้นหนังสือไว้เหมือนเดิมทุกอย่าง

sort() เปลี่ยนลิสต์เดิมโดยตรง (in place)
# sort() modifies the original list
numbers = [5, 2, 8, 1, 9]
numbers.sort()
print(numbers) # [1, 2, 5, 8, 9] - the list itself changed
ผลลัพธ์
[1, 2, 5, 8, 9]
sorted() สร้างลิสต์ใหม่
# sorted() returns a NEW sorted list
numbers = [5, 2, 8, 1, 9]
result = sorted(numbers)
print(result) # [1, 2, 5, 8, 9] - a new list
print(numbers) # [5, 2, 8, 1, 9] - original unchanged!
ผลลัพธ์
[1, 2, 5, 8, 9]
[5, 2, 8, 1, 9]
บั๊ก: เก็บผลลัพธ์ของ sort() ไว้ในตัวแปร
names = ['Charlie', 'Alice', 'Bob']
result = names.sort()
print(result)  # None!
วิธีแก้: ใช้ sorted() เพื่อได้ลิสต์ใหม่
names = ['Charlie', 'Alice', 'Bob']
result = sorted(names)
print(result)  # ['Alice', 'Bob', 'Charlie']

ยังมีความแตกต่างสำคัญอีกข้อ: sorted() ใช้ได้กับออบเจกต์ที่วนซ้ำได้ (iterable) ทุกชนิด ทั้งสตริง ทูเพิล ดิกชันนารี หรือแม้แต่เจเนอเรเตอร์ (generator) ส่วนเมธอด sort() ใช้ได้กับลิสต์เท่านั้น

sorted() ใช้ได้กับ iterable ทุกชนิด
# sorted() works on strings too
letters = sorted("python")
print(letters) # ['h', 'n', 'o', 'p', 't', 'y']
# sorted() works on tuples
coords = sorted((3, 1, 4, 1, 5))
print(coords) # [1, 1, 3, 4, 5] - result is always a list
ผลลัพธ์
['h', 'n', 'o', 'p', 't', 'y']
[1, 1, 3, 4, 5]

วิธีเรียงลำดับแบบย้อนกลับ

ทั้ง sort() และ sorted() รับพารามิเตอร์ reverse ถ้ากำหนดเป็น True จะเรียงจากมากไปน้อยแทนที่จะเรียงจากน้อยไปมาก

เรียงจากน้อยไปมากและจากมากไปน้อย
scores = [72, 95, 88, 61, 100]
# Ascending (default)
print(sorted(scores)) # [61, 72, 88, 95, 100]
# Descending
print(sorted(scores, reverse=True)) # [100, 95, 88, 72, 61]
ผลลัพธ์
[61, 72, 88, 95, 100]
[100, 95, 88, 72, 61]

โดยค่าเริ่มต้น สตริงจะเรียงตามลำดับตัวอักษร เมื่อใช้ reverse=True จะเรียงตามลำดับตัวอักษรแบบย้อนกลับ

เรียงสตริงแบบย้อนกลับ
fruits = ['banana', 'apple', 'cherry', 'date']
print(sorted(fruits)) # ['apple', 'banana', 'cherry', 'date']
print(sorted(fruits, reverse=True)) # ['date', 'cherry', 'banana', 'apple']
ผลลัพธ์
['apple', 'banana', 'cherry', 'date']
['date', 'cherry', 'banana', 'apple']

ฟังก์ชัน key ในการเรียงลำดับของ Python คืออะไร

บางครั้งคุณไม่ได้ต้องการเรียงตามค่าของข้อมูลโดยตรง แต่ต้องการเรียงตามคุณสมบัติบางอย่างของแต่ละค่า เช่น เรียงคำตามความยาวแทนที่จะเรียงตามตัวอักษร

พารามิเตอร์ key ให้คุณส่งฟังก์ชันที่แปลงข้อมูลแต่ละตัวก่อนนำไปเปรียบเทียบ Python จะเรียกฟังก์ชันนี้กับข้อมูลทุกตัว แล้วเรียงลำดับตามผลลัพธ์ที่ได้ ข้อมูลต้นฉบับยังเหมือนเดิม มีเพียงลำดับเท่านั้นที่เปลี่ยน

เรียงตามความยาวสตริงด้วย key=len
# Sort words by length instead of alphabetically
words = ['banana', 'pie', 'strawberry', 'kiwi']
result = sorted(words, key=len)
print(result) # ['pie', 'kiwi', 'banana', 'strawberry']
ผลลัพธ์
['pie', 'kiwi', 'banana', 'strawberry']

ในตัวอย่างนี้ len คือฟังก์ชัน key Python เรียก len() กับแต่ละคำแล้วใช้ความยาวเหล่านั้นตัดสินลำดับ "pie" มี 3 ตัวอักษร "kiwi" มี 4 ตัวอักษร และต่อไปเรื่อย ๆ

คุณใช้ฟังก์ชันใดก็ได้เป็น key ตัวอย่างที่พบบ่อยคือการเรียงโดยไม่สนตัวพิมพ์ใหญ่เล็กด้วย str.lower

เรียงโดยไม่สนตัวพิมพ์ใหญ่เล็ก
# Without key - uppercase letters sort before lowercase
names = ['alice', 'Bob', 'CHARLIE', 'dave']
print(sorted(names)) # ['Bob', 'CHARLIE', 'alice', 'dave']
# With key=str.lower - true alphabetical order
print(sorted(names, key=str.lower)) # ['alice', 'Bob', 'CHARLIE', 'dave']
ผลลัพธ์
['Bob', 'CHARLIE', 'alice', 'dave']
['alice', 'Bob', 'CHARLIE', 'dave']

วิธีเรียงลำดับด้วยฟังก์ชัน lambda

ฟังก์ชันในตัว (built-in) เช่น len และ str.lower ช่วยได้แค่ระดับหนึ่ง เมื่อต้องการตรรกะการเรียงลำดับแบบกำหนดเอง ให้ใช้ lambda ซึ่งเป็นฟังก์ชันเล็ก ๆ ไม่มีชื่อ เขียนได้ในบรรทัดเดียว

lambda รับข้อมูลหนึ่งตัวแล้วคืนค่าที่ใช้ในการเรียง รูปแบบการเขียนคือ lambda item: expression

เรียงตามหลักหน่วยด้วย lambda
# Sort numbers by their last digit
numbers = [23, 45, 12, 67, 34, 89]
result = sorted(numbers, key=lambda x: x % 10)
print(result) # [12, 23, 34, 45, 67, 89]
ผลลัพธ์
[12, 23, 34, 45, 67, 89]

lambda lambda x: x % 10 รับตัวเลขแต่ละตัวแล้วคืนหลักหน่วยของมัน (เศษจากการหารด้วย 10) จากนั้น Python เรียงตามหลักหน่วยเหล่านั้น: 2, 3, 4, 5, 7, 9

เรียงตามตัวอักษรตัวสุดท้าย
# Sort strings by their last character
words = ['hello', 'zebra', 'apple', 'mango']
result = sorted(words, key=lambda w: w[-1])
print(result) # ['zebra', 'apple', 'mango', 'hello']
ผลลัพธ์
['zebra', 'apple', 'hello', 'mango']
เรียงตามหลายเกณฑ์ด้วยทูเพิล
# Sort by length first, then alphabetically
words = ['cat', 'bat', 'elephant', 'ant', 'dog']
result = sorted(words, key=lambda w: (len(w), w))
print(result) # ['ant', 'bat', 'cat', 'dog', 'elephant']
ผลลัพธ์
['ant', 'bat', 'cat', 'dog', 'elephant']

วิธีเรียงดิกชันนารีและข้อมูลที่ซับซ้อน

ในโปรแกรมจริง คุณแทบไม่ได้เรียงลิสต์ของตัวเลขธรรมดา ส่วนใหญ่จะเรียงลิสต์ของดิกชันนารี (dictionary) เช่น เรียงนักเรียนตามคะแนน หรือเรียงสินค้าตามราคา

เรียงลิสต์ของดิกชันนารีตามคีย์
students = [
{'name': 'Alice', 'grade': 92},
{'name': 'Bob', 'grade': 85},
{'name': 'Charlie', 'grade': 97},
{'name': 'Diana', 'grade': 88},
]
# Sort by grade (ascending)
by_grade = sorted(students, key=lambda s: s['grade'])
for s in by_grade:
print(f"{s['name']}: {s['grade']}")
ผลลัพธ์
Bob: 85
Diana: 88
Alice: 92
Charlie: 97
เรียงดิกชันนารีจากมากไปน้อย
students = [
{'name': 'Alice', 'grade': 92},
{'name': 'Bob', 'grade': 85},
{'name': 'Charlie', 'grade': 97},
{'name': 'Diana', 'grade': 88},
]
# Sort by grade (highest first)
top_students = sorted(students, key=lambda s: s['grade'], reverse=True)
for s in top_students:
print(f"{s['name']}: {s['grade']}")
ผลลัพธ์
Charlie: 97
Alice: 92
Diana: 88
Bob: 85

คุณยังเรียงคีย์หรือค่าของดิกชันนารีเองได้ด้วย โดยค่าเริ่มต้น ฟังก์ชัน sorted() จะวนซ้ำผ่านคีย์ของดิกชันนารี

เรียงคีย์และค่าของดิกชันนารี
prices = {'banana': 0.25, 'apple': 0.75, 'cherry': 3.25, 'date': 1.75}
# Sort by key (fruit name)
for fruit in sorted(prices):
print(f"{fruit}: ${prices[fruit]}")
print()
# Sort by value (price)
for fruit in sorted(prices, key=lambda f: prices[f]):
print(f"{fruit}: ${prices[fruit]}")
ผลลัพธ์
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) หมายความว่าเมื่อข้อมูลสองตัวมีค่าเท่ากันตามคีย์ที่ใช้เรียง ข้อมูลทั้งสองจะคงลำดับเดิมไว้

เรื่องนี้สำคัญมากในทางปฏิบัติ ถ้าคุณเรียงนักเรียนตามคะแนน และมีนักเรียนสองคนได้คะแนนเท่ากัน ทั้งสองคนจะอยู่ในลำดับเดิมเหมือนก่อนเรียง

การเรียงแบบเสถียรคงลำดับเดิมของข้อมูลที่มีค่าเท่ากัน
students = [
{'name': 'Alice', 'grade': 90},
{'name': 'Bob', 'grade': 85},
{'name': 'Charlie', 'grade': 90},
{'name': 'Diana', 'grade': 85},
]
# Sort by grade - ties keep original order
result = sorted(students, key=lambda s: s['grade'])
for s in result:
print(f"{s['name']}: {s['grade']}")
# Bob: 85, Diana: 85, Alice: 90, Charlie: 90
# Bob comes before Diana (original order preserved)
# Alice comes before Charlie (original order preserved)
ผลลัพธ์
Bob: 85
Diana: 85
Alice: 90
Charlie: 90

แบบฝึกหัด

เรียงลิสต์ของตัวเลข
เขียนโค้ด

คุณมีลิสต์คะแนนสอบ ให้แสดงคะแนนที่เรียงจากน้อยไปมาก แล้วในบรรทัดถัดไปให้แสดงคะแนนที่เรียงจากมากไปน้อย

ใช้ sorted() เพื่อให้ลิสต์ต้นฉบับไม่เปลี่ยนแปลง

scores = [78, 92, 65, 88, 95, 71]
# Print scores low to high
# Print scores high to low
ทายผลลัพธ์: sort() กับ sorted()
ทายผลลัพธ์

อ่านโค้ดด้านล่างให้ละเอียด โค้ดจะแสดงอะไร อย่าลืมความแตกต่างสำคัญระหว่าง sort() และ sorted()

พิมพ์ผลลัพธ์ให้ตรงทุกตัวอักษร

nums = [3, 1, 4, 1, 5]
result = nums.sort()
print(result)
print(nums)
เรียงคำตามความยาว
เขียนโค้ด

เรียงลิสต์ของคำตามความยาว (สั้นที่สุดก่อน) ถ้ามีสองคำที่ยาวเท่ากัน ให้คงลำดับเดิมไว้

แสดงลิสต์ที่เรียงแล้ว

words = ['elephant', 'cat', 'giraffe', 'ox', 'bear', 'ant']
# Sort by length and print
แก้บั๊ก: การเรียงที่แยกตัวพิมพ์ใหญ่เล็ก
แก้บั๊ก

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

ผลลัพธ์ควรเป็น: ['alice', 'Bob', 'Charlie', 'dave', 'Eve']

names = ['Charlie', 'alice', 'Eve', 'dave', 'Bob']
result = sorted(names)
print(result)
เรียงนักเรียนตามคะแนน
เขียนโค้ด

เรียงลิสต์นักเรียนตามคะแนนจากมากไปน้อย แล้วแสดงชื่อและคะแนนของนักเรียนแต่ละคนทีละบรรทัดในรูปแบบ: Name: Grade

students = [
{'name': 'Alice', 'grade': 88},
{'name': 'Bob', 'grade': 95},
{'name': 'Charlie', 'grade': 72},
{'name': 'Diana', 'grade': 91},
]
# Sort by grade (highest first) and print each student
เรียงตามหลายเกณฑ์
เขียนโค้ด

เรียงลิสต์สินค้าตามหมวดหมู่ก่อน (ตามลำดับตัวอักษร) แล้วเรียงตามราคา (ถูกที่สุดก่อน) ภายในแต่ละหมวดหมู่

แสดงสินค้าแต่ละรายการทีละบรรทัดในรูปแบบ: category: name - $price

products = [
{'name': 'Banana', 'category': 'fruit', 'price': 1.20},
{'name': 'Milk', 'category': 'dairy', 'price': 3.50},
{'name': 'Apple', 'category': 'fruit', 'price': 0.80},
{'name': 'Cheese', 'category': 'dairy', 'price': 5.00},
{'name': 'Cherry', 'category': 'fruit', 'price': 4.00},
{'name': 'Yogurt', 'category': 'dairy', 'price': 2.50},
]
# Sort by category, then by price within each category

บทเรียนที่เกี่ยวข้อง

บทเรียนนี้มีประโยชน์หรือไม่