Computer Programming 1 (Python) • Problem Solving & Mini-Projects

Building JSON Address Book — I can build a simple JSON-based address book (list of dicts) with lookup and update at my level.

Learn to store, search, and update contact information using Python lists, dictionaries, and JSON files.

A Python coding terminal with a program flow and laptop.
Inside this lesson 10 sections 5 questions 4 figures

SECTION 1

Historical Context & Motivation

People have been keeping address books for centuries — handwritten ledgers full of names, addresses, and phone numbers. As computers became mainstream, the challenge shifted to figuring out how to represent that same contact data in a format machines could understand. Early computer systems stored contacts in flat text files with rigid column widths, which made editing and searching slow and error-prone.

The invention of JSON (JavaScript Object Notation) in the early 2000s changed everything. JSON gave developers a lightweight, human-readable way to structure data using simple key-value pairs. Today, virtually every app on your phone — from your contacts list to social media — uses JSON behind the scenes to store and exchange information.

1970s • Flat File Databases

Early contact managers stored data in plain text files with fixed-width columns. Searching required reading the entire file line by line.

1990s • XML Emerges

XML provided a structured, tagged format for data, but it was verbose and difficult to read. A simple contact record could be dozens of lines long.

2001 • JSON Is Born

Douglas Crockford popularized JSON as a lightweight data-interchange format. Its simple syntax — curly braces for objects and square brackets for arrays — made it immediately accessible.

2010s • JSON Becomes Universal

JSON became the default format for web APIs, mobile apps, and configuration files. Python added built-in support through its json module.

Today • JSON Everywhere

From phone contacts to cloud databases, JSON structures underpin nearly every modern application you interact with daily.

The core question this lesson addresses is straightforward: how do you organize a collection of contacts in Python so you can save them to a file, look someone up quickly, and update their information? By building a JSON-based address book, you'll master the practical combination of lists, dictionaries, and file I/O that appears in real-world software every day.

SECTION 2

Core Principles & Definitions

Before we start coding, let's nail down the foundational ideas that make a JSON address book work. Each concept builds on Python data structures you've already seen — lists and dictionaries — and connects them to the JSON file format for persistent storage.

1

Dictionary as a Contact Record

A Python dictionary (dict) maps keys like "name", "phone", and "email" to their values. Each dict represents one person's contact information.
2

List of Dicts as the Address Book

A Python list holds all your contact dictionaries in one ordered collection. You can loop through it, search it, or add new entries at any time.
3

JSON for Persistence

JSON (JavaScript Object Notation) is a text-based file format that mirrors Python's lists and dicts almost perfectly. Python's json module converts between Python objects and JSON files with just a few lines of code.
4

Lookup — Finding a Contact

Lookup means iterating through the list of dicts and checking whether a specific key (like "name") matches the value you're searching for. This is a linear search through the collection.
5

Update — Modifying a Contact

Update means finding the right dict in the list and changing one or more of its values. Because dicts are mutable, you can reassign any key's value directly.
✦

KEY TAKEAWAY

Think of a JSON address book like a filing cabinet. Each dictionary is a single contact card with labeled fields (name, phone, email). The list is the drawer that holds all the cards in order. And the JSON file is the cabinet itself — it keeps your data safe on disk even after you close the program.

SECTION 3

Visual Explanation — Data Structure Map

The diagram below shows how a Python list of dictionaries maps directly to JSON syntax. Notice how Python's [] brackets become JSON arrays and Python's {} braces become JSON objects. The structure is nearly identical, which is why Python's json module can convert between them so easily.

Python List of Dicts → JSON FilePython (in memory)address_book = [{"name": "Alice Smith","phone": "555-0101","email": "alice@mail.com"},{"name": "Bob Jones","phone": "555-0202","email": "bob@mail.com"},{"name": "Carol Lee","phone": "555-0303","email": "carol@mail.com"}]json.dump()json.load()contacts.json (on disk)[{"name": "Alice Smith","phone": "555-0101","email": "alice@mail.com"},{"name": "Bob Jones","phone": "555-0202","email": "bob@mail.com"},{"name": "Carol Lee","phone": "555-0303","email": "carol@mail.com"}]
The left side shows a Python list containing three dictionaries in memory. The green arrows represent json.dump() (writing to disk) and json.load() (reading from disk). The right side shows the resulting contacts.json file. Notice the nearly identical syntax.

The key insight from this diagram is that Python dicts and JSON objects share the same structure: curly braces wrapping key-value pairs. Likewise, Python lists and JSON arrays both use square brackets to hold ordered collections. This natural mapping is what makes Python's json module so convenient — you don't need to write any custom parsing code.

SECTION 4

How It Works — The Core Operations

Every address book application relies on four fundamental operations, sometimes called CRUD: Create, Read, Update, and Delete. In this lesson we'll focus on three of those — creating contacts, looking them up (reading), and updating their data. Let's walk through the code patterns for each operation.

Creating the Address Book & Adding Contacts

You start by importing the json module and defining an empty list. Each time you want to add a contact, you build a dictionary with the appropriate keys and append it to the list. Here is the pattern:

💡

Code Pattern — Add a Contact

import json address_book = [] def add_contact(book, name, phone, email): contact = { "name": name, "phone": phone, "email": email } book.append(contact) add_contact(address_book, "Alice Smith", "555-0101", "alice@mail.com") add_contact(address_book, "Bob Jones", "555-0202", "bob@mail.com")

Looking Up a Contact

A lookup searches through the list for a dictionary whose "name" key matches the search term. We use a for loop that returns the matching dict, or None if no match is found. This is a linear search — it checks each contact one by one, which works well for small-to-medium address books.

💡

Code Pattern — Lookup

def lookup_contact(book, name): for contact in book: if contact["name"].lower() == name.lower(): return contact return None result = lookup_contact(address_book, "alice smith") if result: print(result["phone"]) # Output: 555-0101 else: print("Contact not found.")

Updating a Contact

An update combines the lookup step with direct dictionary assignment. Once you find the right contact, you simply reassign the value for any key you want to change. Because Python dicts are mutable (changeable in place), the modification applies immediately to the list without needing to remove and re-add the entry.

💡

Code Pattern — Update

def update_contact(book, name, field, new_value): contact = lookup_contact(book, name) if contact: contact[field] = new_value return True return False update_contact(address_book, "Alice Smith", "phone", "555-9999")

Saving & Loading with JSON

💡

Code Pattern — Save & Load

def save_book(book, filename="contacts.json"): with open(filename, "w") as f: json.dump(book, f, indent=2) def load_book(filename="contacts.json"): with open(filename, "r") as f: return json.load(f) save_book(address_book) loaded = load_book() print(loaded[0]["name"]) # Output: Alice Smith

The json.dump() function serializes (converts) your Python list into JSON text and writes it to a file. The indent=2 argument makes the output human-readable with nice formatting. The json.load() function does the reverse — it reads the file and deserializes (converts back) the JSON text into a Python list of dictionaries.

SECTION 5

Data Flow & Program Architecture

Now that you understand each individual operation, let's zoom out and look at how these pieces fit together into a complete program. The diagram below shows the typical flow of a menu-driven address book application. The user picks an action, the program performs it, and then loops back to the menu.

Address Book Program FlowSTART: Load JSONShow MenuUser Choice?AddAdd ContactLookupLookup ContactUpdateUpdate ContactSave JSON & Looploop back
This flowchart shows the main program loop. The application starts by loading existing contacts from the JSON file, displays a menu, and branches based on the user's choice (Add, Lookup, or Update). After each operation, the program saves the updated data back to the JSON file and loops back to the menu.

Notice the loop structure in the diagram. The program doesn't exit after a single operation — it returns to the menu so the user can perform multiple actions in one session. This event loop pattern is extremely common in real applications. Also notice that saving happens after every change, ensuring data is never lost even if the program crashes unexpectedly.

ℹ

Complete Menu Loop Code

def main(): try: book = load_book() except FileNotFoundError: book = [] while True: print("\n--- Address Book ---") print("1. Add contact") print("2. Lookup contact") print("3. Update contact") print("4. Quit") choice = input("Choose an option: ") if choice == "1": name = input("Name: ") phone = input("Phone: ") email = input("Email: ") add_contact(book, name, phone, email) save_book(book) print("Contact added!") elif choice == "2": name = input("Search name: ") result = lookup_contact(book, name) if result: print(f"Name: {result['name']}") print(f"Phone: {result['phone']}") print(f"Email: {result['email']}") else: print("Not found.") elif choice == "3": name = input("Name to update: ") field = input("Field (phone/email): ") value = input("New value: ") if update_contact(book, name, field, value): save_book(book) print("Updated!") else: print("Contact not found.") elif choice == "4": print("Goodbye!") break main()

SECTION 6

Worked Example — Full Session

Let's trace through a complete session where we create an address book, add two contacts, look one up, update a phone number, and save everything to a JSON file. Follow each step carefully to see how the data changes.

Building and Using an Address Book

  1. 1

    Step 1 — Initialize the Address Book

    We start by importing the json module and creating an empty list. At this point, address_book is [] — no contacts yet.
    address_book = [] → empty list, length 0
  2. 2

    Step 2 — Add Two Contacts

    We call add_contact(address_book, "Alice Smith", "555-0101", "alice@mail.com") and then add_contact(address_book, "Bob Jones", "555-0202", "bob@mail.com"). Each call creates a new dict and appends it to the list.
    address_book now has 2 entries: index 0 is Alice, index 1 is Bob.
  3. 3

    Step 3 — Look Up Bob

    We call lookup_contact(address_book, "bob jones"). The function loops through each dict. On index 0 it checks "Alice Smith".lower() == "bob jones" → False. On index 1 it checks "Bob Jones".lower() == "bob jones" → True. It returns Bob's dict.
    Returns {"name": "Bob Jones", "phone": "555-0202", "email": "bob@mail.com"}
  4. 4

    Step 4 — Update Alice's Phone Number

    We call update_contact(address_book, "Alice Smith", "phone", "555-9999"). Internally, this calls lookup_contact to find Alice's dict, then sets contact["phone"] = "555-9999". Because dicts are mutable, this changes the data inside the list directly.
    Alice's phone is now 555-9999 instead of 555-0101.
  5. 5

    Step 5 — Save to JSON

    We call save_book(address_book). This opens contacts.json in write mode and calls json.dump() with indent=2. The file now contains the updated address book in clean, formatted JSON.
    File contacts.json written to disk with 2 contacts, Alice's phone updated.

SECTION 7

Strengths, Limitations & Alternatives

A JSON-based address book is a great starting project, but it's important to understand where it shines and where it falls short. Comparing it to alternative approaches helps you appreciate what you've built and where you might go next.

CriterionJSON File (Our Approach)CSV FileSQL Database
Human-readableYes — nicely formatted with indentationYes — comma-separated rowsNo — binary format, needs special tools
Nested dataYes — dicts can contain lists or other dictsNo — flat rows onlyRequires multiple tables with joins
Search speedSlow for large data (linear search)Slow for large data (linear search)Fast — indexed queries
Setup complexityMinimal — just import jsonMinimal — import csvModerate — requires SQL knowledge
Best forSmall projects, config files, APIsSpreadsheet-like dataLarge apps with thousands of records

Comparison of data storage approaches for contact management

✦

KEY TAKEAWAY

Think of choosing a storage format like choosing a vehicle. A JSON file is like a bicycle — lightweight, easy to start using, and perfect for short trips. A SQL database is like a truck — more powerful and scalable, but it takes more effort to learn to drive. For an address book with a few hundred contacts, the bicycle is the right choice.

SECTION 8

Connection to Advanced Concepts

The JSON address book you've built uses concepts that scale directly into professional software development. Understanding how your mini-project connects to more advanced tools will help you see where this learning path leads.

What You LearnedAdvanced Version
List of dicts in memoryORM models (e.g., SQLAlchemy) that map Python objects to database rows
Linear search with a for loopDatabase indexing and SQL WHERE clauses for instant lookups
json.dump() / json.load()REST API serialization frameworks (e.g., Flask-RESTful, Django REST)
Menu-driven text interfaceWeb front-end with HTML forms, or a GUI with Tkinter or PyQt
try/except for missing fileComprehensive error handling, logging, and input validation

From beginner project to professional tools

When you're ready for the next level, consider adding a delete function (completing CRUD), implementing input validation to reject empty names or improperly formatted emails, or even building a simple web interface using Flask. Each of these extensions uses the same structural patterns — lists, dicts, and JSON — that you've just learned.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain in your own words why a list of dictionaries is a good data structure for an address book. Why not just use a single dictionary with names as keys?
Show answer
A list of dictionaries is flexible because each dictionary can hold multiple fields (name, phone, email) for one contact, and the list groups them all together in order. Using a single dictionary with names as keys might seem simpler, but it breaks if two people share the same name — the second entry would overwrite the first. The list-of-dicts approach avoids this collision problem and also makes it easy to iterate over all contacts, add new ones with append(), and extend each contact with additional fields later.

Question 2: BASIC CALCULATION

Write a function called count_contacts(book) that takes the address book list and returns the number of contacts it contains. Then write a call to it with an address book that has 3 entries and show the expected output.
Show answer
The function is straightforward: def count_contacts(book): return len(book). If you call print(count_contacts(address_book)) on a list with 3 dicts, the output is 3. The built-in len() function counts the number of items in a list, and since each item is one contact dict, this gives us the contact count directly.

Question 3: INTERMEDIATE

Write a function called delete_contact(book, name) that removes a contact by name (case-insensitive). The function should return True if the contact was found and deleted, or False if no match was found. Hint: you can use a loop with an index.
Show answer
One clean approach uses enumerate() to track the index while looping: def delete_contact(book, name): for i, contact in enumerate(book): if contact["name"].lower() == name.lower(): book.pop(i) return True return False. The pop(i) method removes the element at index i from the list. We return immediately after deletion because continuing to loop over a modified list can cause bugs. If the loop completes without finding a match, we return False.

Question 4: APPLIED

You are building an address book for a school club with 50 members. You want to add an "address" field to each contact that includes street, city, state, and zip. Show how you would structure a single contact dict with this nested address, and explain what advantage JSON has over CSV for storing this kind of data.
Show answer
A contact with a nested address would look like: {"name": "Dana Park", "phone": "555-0404", "email": "dana@school.edu", "address": {"street": "123 Oak St", "city": "Springfield", "state": "IL", "zip": "62704"}}. JSON handles this naturally because values in a JSON object can themselves be objects (nested dicts). A CSV file can only store flat rows — you'd have to flatten the address into separate columns like address_street, address_city, etc., which becomes messy if you later want to add more nested structures. JSON preserves the logical grouping of related data.

Question 5: CRITICAL THINKING

Your address book currently uses linear search to find contacts, checking each entry one by one. If the address book grows to 10,000 contacts, this becomes slow. Without switching to a database, propose a way to reorganize the data structure in Python so that looking up a contact by name is much faster. What trade-off would this introduce when adding new contacts or handling duplicate names?
Show answer
Instead of a list of dicts, you could use a dictionary of dicts where the key is the contact's name: address_book = {"Alice Smith": {"phone": "555-0101", "email": "alice@mail.com"}}. Dictionary key lookup in Python is O(1) on average (constant time), compared to O(n) for a list scan. This makes lookups nearly instant regardless of size. The trade-off is that dictionary keys must be unique, so you can't store two people with the exact same name without modifying the key (e.g., appending a number). You also lose the guaranteed ordering you get with a list, though in Python 3.7+ dicts do maintain insertion order. This trade-off between speed and flexibility is a fundamental concept in computer science known as choosing the right data structure for your use case.

SUMMARY

Summary

Summary

In this lesson, you built a complete JSON-based address book using Python. You learned that a dictionary represents a single contact with key-value pairs like name, phone, and email. A list of dictionaries serves as the full address book, holding all contacts in an ordered collection. The json module provides json.dump() and json.load() to save data to disk and load it back, giving your program persistence across sessions.

You implemented three core operations: adding contacts by appending dicts to the list, looking up contacts with a case-insensitive linear search, and updating contacts by finding the right dict and reassigning its values. You also structured the whole program around a menu-driven loop — the same pattern used in real-world applications. These skills form the foundation for more advanced projects involving databases, web APIs, and graphical interfaces.

Varsity Tutors • Computer Programming 1 (Python) • Building JSON Address Book