EP07: โครงสร้างข้อมูลขั้นสูง: ดิกชันนารีและเซต

(1) ทบทวนความรู้เดิม

  • ลิสต์ (Lists) และ ทูเพิล (Tuples):
    • ความแตกต่างเรื่องการเปลี่ยนแปลงค่า (Mutability: Lists เปลี่ยนได้, Tuples เปลี่ยนไม่ได้)
    • การเข้าถึงข้อมูลตามลำดับดัชนี (Index-based access)
    • การวนซ้ำ (Iteration) ด้วย for loop

(2) ดิกชันนารี (Dictionaries) ใน Python: โครงสร้างข้อมูลแบบ Key-Value 📖

  • คุณสมบัติหลัก:
    • คู่ Key-Value (Key-Value Pair): เก็บข้อมูลเป็นคู่ๆ โดยแต่ละคู่ประกอบด้วย คีย์ (Key) ที่ไม่ซ้ำกันและใช้สำหรับอ้างอิง และ ค่า (Value) ที่เป็นข้อมูลจริง
    • คีย์ (Keys):
      • ต้องไม่ซ้ำกัน (Unique) ภายในดิกชันนารีเดียว
      • ต้องเป็นข้อมูลประเภทที่เปลี่ยนแปลงไม่ได้ (Immutable) เช่น สตริง (str), ตัวเลข (int, float), หรือทูเพิล (tuple) ที่สมาชิกภายในเป็น Immutable ทั้งหมด
    • ค่า (Values): สามารถเป็นข้อมูลชนิดใดก็ได้ และสามารถซ้ำกันได้
    • เปลี่ยนแปลงได้ (Mutable): สามารถเพิ่ม, ลบ, หรือแก้ไขคู่ Key-Value ได้หลังจากการสร้าง
    • ลำดับ (Ordering): ใน Python เวอร์ชัน 3.7 ขึ้นไป ดิกชันนารีจะ จำลำดับการใส่ข้อมูล (Insertion Order) แต่ในเวอร์ชันก่อนหน้านั้นถือว่าเป็นโครงสร้างข้อมูลที่ไม่มีลำดับ (Unordered)
  • การสร้างดิกชันนารี (Creating Dictionaries):
    • ใช้เครื่องหมายวงเล็บปีกกา {}:
      • ดิกชันนารีว่าง: empty_dict = {}
      • ดิกชันนารีพร้อมสมาชิก: student = {"id": "66001", "name": "สมชาย", "gpa": 3.25}
    • ใช้ฟังก์ชัน dict():
      • another_dict = dict(id="66002", name="สมหญิง", gpa=3.50)
      • from_pairs = dict([("course", "Python"), ("credits", 3)])
  • การเข้าถึงค่า (Accessing Values):
    • ใช้คีย์ในเครื่องหมาย []:student["name"] จะคืนค่า "สมชาย"
      • หากคีย์นั้นไม่มีอยู่ในดิกชันนารี จะเกิดข้อผิดพลาด KeyError
    • ใช้เมธอด get(key):student.get("gpa") จะคืนค่า 3.25
      • หากคีย์ไม่มีอยู่ จะคืนค่า None (โดยไม่เกิด Error)
    • ใช้เมธอด get(key, default_value): student.get("major", "ยังไม่ระบุ") ถ้าคีย์ “major” ไม่มีอยู่ จะคืนค่า “ยังไม่ระบุ”
  • การเพิ่มและแก้ไขข้อมูล (Adding and Modifying Entries):
    • เพิ่มคู่ Key-Value ใหม่: student["major"] = "Computer Science"
    • แก้ไขค่าของคีย์ที่มีอยู่: student["gpa"] = 3.30
    • ใช้เมธอด update(other_dict_or_iterable_of_pairs):
      • student.update({"email": "[email protected]", "gpa": 3.35}) (แก้ไข gpa และเพิ่ม email)
  • การลบข้อมูล (Removing Entries):
    • pop(key): ลบคู่ Key-Value ที่มีคีย์ตรงกับ key และคืนค่า (Value) ของคู่นั้นออกมา (ถ้าคีย์ไม่มีอยู่จะเกิด KeyError)
      • removed_gpa = student.pop("gpa")
    • pop(key, default_value): ถ้าคีย์ไม่มีอยู่ จะคืน default_value แทนการเกิด Error
    • popitem(): ลบและคืนค่าคู่ Key-Value คู่ล่าสุด ที่เพิ่มเข้าไป (ใน Python 3.7+) หรือคู่ใดคู่หนึ่งแบบสุ่ม (ในเวอร์ชันเก่ากว่า) (ถ้าดิกชันนารีว่างจะเกิด KeyError)
    • del my_dict[key]: ลบคู่ Key-Value ที่มีคีย์ตรงกับ key (ถ้าคีย์ไม่มีอยู่จะเกิด KeyError)
      • del student["id"]
    • clear(): ลบสมาชิกทั้งหมดออกจากดิกชันนารี ทำให้เป็นดิกชันนารีว่าง
  • การวนซ้ำกับดิกชันนารี (Iterating through Dictionaries):
    • วนซ้ำผ่านคีย์ (Keys) (เป็นพฤติกรรมเริ่มต้น):
Python
for k in student:
    print(f"Key: {k}, Value: {student[k]}")
  • วนซ้ำผ่านคีย์โดยใช้เมธอด keys():
Python
for k in student.keys():
    print(k)
  • วนซ้ำผ่านค่า (Values) โดยใช้เมธอด values():
Python
for v in student.values():
    print(v)
  • วนซ้ำผ่านทั้งคีย์และค่า (Items) โดยใช้เมธอด items():
Python
for k, v in student.items():
    print(f"รายละเอียด: {k} -> {v}")
  • การตรวจสอบการมีอยู่ของคีย์ (Checking for Keys): ใช้ in หรือ not in
    • "name" in student (จะคืน True)
    • "tel" not in student (จะคืน True)
  • ฟังก์ชันและเมธอดสำคัญอื่นๆ:
    • len(my_dict): คืนจำนวนคู่ Key-Value ในดิกชันนารี

(3) เซต (Sets) ใน Python: กลุ่มของข้อมูลที่ไม่ซ้ำกัน 🛡️

  • คุณสมบัติหลัก:
    • สมาชิกไม่ซ้ำกัน (Unique Elements): เซตจะเก็บสมาชิกแต่ละตัวเพียงครั้งเดียว หากพยายามเพิ่มสมาชิกที่มีอยู่แล้วเข้าไปอีกครั้ง เซตจะไม่เปลี่ยนแปลง
    • ไม่มีการเรียงลำดับ (Unordered): สมาชิกในเซตไม่มีลำดับที่แน่นอน ดังนั้นจึงไม่สามารถเข้าถึงสมาชิกด้วยดัชนีตัวเลข (เช่น my_set[0]) ได้
    • เปลี่ยนแปลงสมาชิกได้ (Mutable): สามารถเพิ่มหรือลบสมาชิกออกจากเซตได้หลังจากการสร้าง
    • สมาชิกในเซตต้องเป็น Immutable: สมาชิกแต่ละตัวในเซตต้องเป็นข้อมูลประเภทที่เปลี่ยนแปลงไม่ได้ (เช่น ตัวเลข, สตริง, ทูเพิล) ไม่สามารถมีลิสต์หรือดิกชันนารีเป็นสมาชิกของเซตได้
  • การสร้างเซต (Creating Sets):
    • ใช้เครื่องหมายวงเล็บปีกกา {} กับสมาชิกคั่นด้วยจุลภาค:
      • numbers_set = {1, 2, 3, 2, 4} (ผลลัพธ์จะได้ {1, 2, 3, 4})
      • mixed_set = {1.0, "Hello", (1, 2)}
    • ใช้ฟังก์ชัน set():
      • สร้างเซตว่าง: empty_set = set() (ข้อควรระวัง: การใช้ {} จะเป็นการสร้างดิกชันนารีว่าง ไม่ใช่เซตว่าง)
      • แปลงข้อมูลประเภทอื่น (เช่น ลิสต์, ทูเพิล, สตริง) ให้เป็นเซต (จะมีการตัดสมาชิกที่ซ้ำกันออกโดยอัตโนมัติ):
        • from_list = set([1, 2, 2, 3]) จะได้ {1, 2, 3}
        • from_string = set("banana") จะได้ {'b', 'a', 'n'} (ลำดับอาจไม่เป็นเช่นนี้)
  • การเพิ่มและลบสมาชิก (Adding and Removing Elements):
    • my_set.add(element): เพิ่ม element เข้าไปในเซต (ถ้า element มีอยู่แล้ว จะไม่มีอะไรเกิดขึ้น)
    • my_set.update(iterable): เพิ่มสมาชิกทุกตัวจาก iterable (เช่น ลิสต์, เซตอื่น) เข้าไปในเซต
    • my_set.remove(element): ลบ element ออกจากเซต (ถ้า element ไม่มีอยู่ในเซต จะเกิดข้อผิดพลาด KeyError)
    • my_set.discard(element): ลบ element ออกจากเซต (ถ้า element ไม่มีอยู่ในเซต จะไม่เกิด Error)
    • my_set.pop(): ลบและคืนค่าสมาชิกตัวใดตัวหนึ่งออกจากเซต (เนื่องจากเซตไม่มีลำดับ จึงไม่สามารถระบุได้ว่าจะลบตัวไหน) (ถ้าเซตว่างจะเกิด KeyError)
    • my_set.clear(): ลบสมาชิกทั้งหมดออกจากเซต ทำให้เป็นเซตว่าง
  • การวนซ้ำกับเซต (Iterating through Sets): ใช้ for loop (ลำดับการแสดงผลจะไม่แน่นอน)
Python
colors = {"red", "green", "blue"}
for color in colors:
    print(color)
  • การตรวจสอบการเป็นสมาชิก (Membership Test): ใช้ in หรือ not in (การตรวจสอบในเซตทำได้รวดเร็วมาก)
    • "red" in colors (จะคืน True)
  • การดำเนินการทางเซต (Set Operations): (แนะนำให้วาดภาพ Venn Diagram ประกอบ)
    • ยูเนียน (Union): ผลลัพธ์คือเซตใหม่ที่รวมสมาชิกทั้งหมดจากทั้งสองเซต
      • ตัวดำเนินการ: | (เช่น set1 | set2)
      • เมธอด: set1.union(set2)
    • อินเตอร์เซกชัน (Intersection): ผลลัพธ์คือเซตใหม่ที่ประกอบด้วยสมาชิกที่ปรากฏ ร่วมกัน ในทั้งสองเซตเท่านั้น
      • ตัวดำเนินการ: & (เช่น set1 & set2)
      • เมธอด: set1.intersection(set2)
    • ผลต่าง (Difference): ผลลัพธ์คือเซตใหม่ที่ประกอบด้วยสมาชิกที่อยู่ในเซตแรก แต่ไม่อยู่ ในเซตที่สอง
      • ตัวดำเนินการ: - (เช่น set1 - set2)
      • เมธอด: set1.difference(set2)
    • ผลต่างสมมาตร (Symmetric Difference): ผลลัพธ์คือเซตใหม่ที่ประกอบด้วยสมาชิกที่อยู่ในเซตใดเซตหนึ่ง แต่ไม่อยู่ในทั้งสองเซต (สมาชิกที่ไม่ซ้ำกันของแต่ละเซต)
      • ตัวดำเนินการ: ^ (เช่น set1 ^ set2)
      • เมธอด: set1.symmetric_difference(set2)
  • เมธอดอื่นๆ ที่น่าสนใจสำหรับเซต:
    • set1.issubset(set2): ตรวจสอบว่าสมาชิกทุกตัวของ set1 อยู่ใน set2 หรือไม่ (เป็นเซตย่อย)
    • set1.issuperset(set2): ตรวจสอบว่าสมาชิกทุกตัวของ set2 อยู่ใน set1 หรือไม่ (เป็นเซตใหญ่)
    • set1.isdisjoint(set2): ตรวจสอบว่า set1 และ set2 ไม่มีสมาชิกร่วมกันเลยหรือไม่
  • ประโยชน์หลักของเซต:
    • การลบข้อมูลที่ซ้ำซ้อนออกจากกลุ่มข้อมูล (เช่น ลิสต์): แปลงลิสต์เป็นเซต (ข้อมูลซ้ำจะหายไป) แล้วถ้าต้องการกลับเป็นลิสต์ก็แปลงกลับ
    • การทดสอบการเป็นสมาชิก (Membership Testing): ทำได้อย่างรวดเร็วและมีประสิทธิภาพสูงกว่าการค้นหาในลิสต์ (โดยเฉพาะลิสต์ขนาดใหญ่)
    • การดำเนินการทางคณิตศาสตร์เซต
  • (เสริม) Frozenset: เป็นเซตประเภทที่เปลี่ยนแปลงไม่ได้ (Immutable) สร้างโดยใช้ frozenset(iterable) สามารถใช้เป็นคีย์ในดิกชันนารีได้

  • ปฏิบัติการที่ 1: ดิกชันนารีเพื่อนรัก 📖
    1. โจทย์ 1.1: ข้อมูลส่วนตัวของฉัน:
      • สร้างดิกชันนารีชื่อ my_profile เก็บข้อมูลต่อไปนี้:
        • "name": (ชื่อของคุณ)
        • "age": (อายุของคุณ)
        • "hobbies": (ลิสต์ของงานอดิเรกอย่างน้อย 2 อย่าง เช่น ["reading", "coding"])
        • "city": (เมืองที่อยู่ปัจจุบัน)
      • แสดงผลดิกชันนารี my_profile ทั้งหมด
      • แสดงผลเฉพาะค่าของ “name” และ “age”
      • เพิ่มคีย์ใหม่ "email" พร้อมค่าอีเมลของคุณเข้าไปใน my_profile
      • แก้ไขค่าของ “city” เป็นเมืองอื่น
      • ลบคีย์ “hobbies” ออกจาก my_profile แล้วแสดงผลดิกชันนารีล่าสุด
    2. โจทย์ 1.2: แปลศัพท์ผลไม้ (อังกฤษ-ไทย):
      • สร้างดิกชันนารี fruit_dict เก็บคำศัพท์ผลไม้ภาษาอังกฤษเป็นคีย์ และคำแปลภาษาไทยเป็นค่า อย่างน้อย 5 คู่ (เช่น "apple": "แอปเปิ้ล", "banana": "กล้วย")
      • เขียนโปรแกรมรับชื่อผลไม้ภาษาอังกฤษจากผู้ใช้
      • ใช้เมธอด get() เพื่อค้นหาและแสดงคำแปลภาษาไทย ถ้าไม่พบคำนั้น ให้แสดงว่า “ไม่พบคำศัพท์นี้”
  • ปฏิบัติการที่ 2: ท่องไปในดิกชันนารี 🚶‍♂️🚶‍♀️
    1. โจทย์ 2.1: รายละเอียดสินค้าคงคลัง:
      • สร้างดิกชันนารี inventory = {"sku001": {"name": "ปากกา", "price": 10, "stock": 100}, "sku002": {"name": "ดินสอ", "price": 5, "stock": 150}, "sku003": {"name": "ยางลบ", "price": 7, "stock": 80}} (สังเกตว่าเป็นดิกชันนารีซ้อนดิกชันนารี)
      • เขียน for loop เพื่อวนซ้ำและแสดง รหัสสินค้า (key) ทั้งหมด
      • เขียน for loop เพื่อวนซ้ำและแสดง ข้อมูล (value ซึ่งเป็น dict อีกที) ของสินค้าทั้งหมด
      • เขียน for loop เพื่อวนซ้ำและแสดง ทั้งรหัสสินค้าและชื่อสินค้า ในรูปแบบ: รหัส [รหัสสินค้า]: ชื่อสินค้า [ชื่อสินค้า]
  • ปฏิบัติการที่ 3: เซตแห่งความไม่ซ้ำซ้อน 🛡️
    1. โจทย์ 3.1: เพื่อนในห้อง:
      • สร้างลิสต์ students_list = ["สมชาย", "สมหญิง", "สมชาย", "มานะ", "สมหญิง", "ปิติ"] ที่มีชื่อซ้ำกัน
      • แปลง students_list ให้เป็นเซตชื่อ unique_students เพื่อลบชื่อที่ซ้ำซ้อนออก แล้วแสดงผลเซตนั้น
      • เพิ่มชื่อ “วีณา” เข้าไปในเซต unique_students โดยใช้ add()
      • ลบชื่อ “มานะ” ออกจากเซตโดยใช้ remove() (สมมติว่า “มานะ” มีอยู่)
      • ลองลบชื่อ “สมศักดิ์” (ที่ไม่มีในเซต) โดยใช้ discard() แล้วสังเกตว่าไม่เกิด Error
      • แสดงผลจำนวนสมาชิกในเซต unique_students
    2. โจทย์ 3.2: ตรวจสอบส่วนผสม:
      • สร้างเซต required_ingredients = {"แป้ง", "ไข่", "น้ำตาล"}
      • สร้างลิสต์ my_ingredients = ["ไข่", "นม", "แป้ง", "น้ำตาล", "เกลือ"]
      • แปลง my_ingredients เป็นเซตชื่อ my_ingredients_set
      • ตรวจสอบว่าคุณมีส่วนผสมที่จำเป็นทั้งหมดหรือไม่ (ใช้ issuperset() หรือวนลูปเช็ค in)
  • ปฏิบัติการที่ 4: พลังแห่งการดำเนินการทางเซต ➕➖🤝
    1. โจทย์ 4.1: คนชอบดูหนังกับคนชอบฟังเพลง:
      • movie_lovers = {"Alice", "Bob", "Charlie", "David"}
      • music_lovers = {"Charlie", "David", "Eve", "Frank"}
      • หาและแสดงผล:
        • กลุ่มคนทั้งหมด (Union)
        • คนที่ชอบทั้งดูหนังและฟังเพลง (Intersection)
        • คนที่ชอบดูหนังแต่ไม่ชอบฟังเพลง (Difference: movie_lovers – music_lovers)
        • คนที่ชอบอย่างใดอย่างหนึ่ง แต่ไม่ใช่ทั้งสองอย่าง (Symmetric Difference)
  • แบบฝึกหัดประยุกต์ (เลือกทำ 1 ข้อ หรือเป็นการบ้าน)
    1. โจทย์ประยุกต์ 1: นับจำนวนคำในประโยค (Word Count):
      • คำสั่ง: เขียนโปรแกรมรับประโยคภาษาอังกฤษจากผู้ใช้ (สมมติว่าคำต่างๆ คั่นด้วยเว้นวรรค และเป็นตัวพิมพ์เล็กทั้งหมดเพื่อง่ายต่อการนับ)
      • นับว่าแต่ละคำในประโยคปรากฏกี่ครั้ง
      • แสดงผล: เป็นดิกชันนารี โดยมีคีย์เป็นคำ และค่าเป็นจำนวนครั้งที่คำนั้นปรากฏ
      • ตัวอย่าง Input: "this is a cat and this is a dog"
      • ตัวอย่าง Output (ลำดับอาจไม่เหมือนนี้): {'this': 2, 'is': 2, 'a': 2, 'cat': 1, 'and': 1, 'dog': 1}
      • คำใบ้: ใช้เมธอด split() ของสตริงเพื่อแยกคำ, ใช้ดิกชันนารีในการเก็บผลการนับ
    2. โจทย์ประยุกต์ 2: หาเพื่อนร่วมงานที่มีทักษะตรงกัน:
      • employee1_skills = {"python", "java", "sql", "git"}
      • employee2_skills = {"java", "javascript", "html", "css", "git"}
      • employee3_skills = {"python", "sql", "docker", "aws"}
      • คำสั่ง:
        • หาและแสดงทักษะที่ทั้ง Employee 1 และ Employee 2 มีเหมือนกัน
        • หาและแสดงทักษะทั้งหมดที่ Employee 1 หรือ Employee 3 คนใดคนหนึ่งมี (ไม่นับซ้ำ)
        • ถ้าต้องการหาคนที่สามารถทำงานร่วมกันในโปรเจกต์ที่ต้องใช้ทั้ง “python” และ “git” มีใครบ้าง? (ต้องใช้เงื่อนไข in กับเซตของแต่ละคน)