Contact Book
📇 Contact Book
🎮 What We're Building
A contact book storing name, phone, and email per contact, supporting adding new contacts, searching/listing them, editing an existing contact's details, and deleting one — all persisted to a JSON file between runs.
Step 1: Representing a Contact
Same "plain dict, no class" approach as Chapter 4's tasks — a contact is just three related pieces of string data (Course 1, Chapters 5 and 7).
Step 2: Create — Adding a Contact
Step 3: Read — Listing and Searching
search_contacts reuses a list comprehension (Course 1/2) with a substring in check — .lower() on both sides makes the search case-insensitive, so searching "ada" still finds "Ada Lovelace".
Step 4: Update — Editing a Contact
The same bounds-checked guard clause from Chapter 4's complete_task. contacts[index][field] = new_value reassigns one key on the target contact's dict directly — field can be "name", "phone", or "email", letting one function handle updating any of the three.
Step 5: Delete — Removing a Contact
del contacts[index] reuses del from Course 1, Chapter 7's dictionary coverage — it works on list items too, removing exactly the one element at that position and shifting the rest down.
After delete_contact(contacts, 2), whatever was previously contact #4 is now contact #3 — every index after the deleted one shifts down. If the menu loop (Step 6) ever displayed a list and then asked for a number to act on before re-listing, a stale index could now point at the wrong contact. Always re-list before acting on an index, or (as Chapter 4's own extension suggested) give each contact a persistent ID that never changes regardless of deletions elsewhere in the list.
Step 6: Tying It Together — Load, Save, and the Menu Loop
Identical load/save shape to Chapter 4's to-do app — this pattern (a Path, a load_ function returning [] if the file doesn't exist yet, a save_ function writing the whole list back) is worth recognizing as reusable across almost any small JSON-backed CLI tool, not something unique to contacts or tasks specifically.
🏁 The Complete Contact Book
Note that options 4 and 5 both call list_contacts(contacts) first, before asking for a number — directly applying this chapter's own warn-box advice, so the number the user types always matches what they're currently looking at.
Create
add_contact() — appends a new dict to the list.
Read
list_contacts() / search_contacts() — display or filter.
Update
update_contact() — reassigns one field on an existing dict.
Delete
delete_contact() — del by index, shifting later items down.
Try these on your own:
- Give each contact a persistent
idinstead of relying on list position, fixing this chapter's own warn-box concern properly. - Add a CSV export option using the
csvmodule (Course 2, Chapter 7) so contacts can be opened in a spreadsheet. - Validate that
emailcontains an@symbol before accepting it, printing an error and re-asking otherwise. - Prevent exact duplicate names from being added, warning the user and asking for confirmation first.
🎯 What's Next
Next chapter: Simple Web Scraper — introducing an external library, requests and BeautifulSoup.