MGU Computer Science Series — Programming with Python
Part IV: Data Structures
Order · Uniqueness · Lookup · Mutability · Design
Chapter 20 of 30
20
MGU Computer Science Series  ·  Programming with Python  ·  Library Textbook Edition

Chapter 20:
Choosing and Combining
Data Structures

Comparing lists, tuples, sets, and dictionaries by order, uniqueness, lookup, and mutability — and learning to design a program's data before writing its code

■ Part IV of VII ■

■ 11 Sections  •  Chapter 20 of 30 ■

Consolidation chapter — builds on Chapters 16 through 19

Chapter Introduction and Learning Objectives

You now know Python's four main built-in collection types: lists, tuples, sets, and dictionaries. You have also built nested structures and used them to model real information. What you have not yet practiced is the most important skill that comes after knowing the tools: choosing the right tool for the job.

This chapter teaches you how to think before you code. You will compare structures by their properties, practice designing data representations on paper before writing a line of Python, and learn to recognize when a first design needs to be reworked. By the end, you should be able to look at a problem, sketch a data shape, justify your choice, and write programs that are clearer because the data is organized well.

Opening Question

A teacher wants to track which students have submitted an assignment, a store wants to look up a product's price by name, and a researcher wants to count how many times each word appears in a document. Each of these problems could technically be solved with a list — but each one has a different structure that fits it far better. What separates a program that merely works from a program whose data is organized well? By the end of this chapter, you will have a framework for answering that question every time you sit down to write code.

11Sections
4Structures Compared
12Key Terms
30Total Chapters

Learning Objectives

By the end of this chapter, you should be able to:

  1. 1Review the four main built-in collection types and state their defining properties.
  2. 2Decide whether order matters for a given problem and choose a structure accordingly.
  3. 3Decide whether duplicate values should be allowed, prevented, or counted, and choose a structure accordingly.
  4. 4Distinguish between lookup by position and lookup by key, and explain when each is preferable.
  5. 5Explain the difference between mutable and immutable structures and use that distinction to signal intent in code.
  6. 6Model a real-world record using either a tuple or a dictionary, and justify the choice.
  7. 7Combine structures safely using common patterns: list of dictionaries, dictionary of lists, and dictionary of dictionaries.
  8. 8Compare tradeoffs among lists, tuples, sets, and dictionaries for a given task.
  9. 9Sketch a data structure on paper before writing code, and use that sketch to guide implementation.
  10. 10Recognize signs that a data structure needs to be refactored, including parallel lists and lookup loops.
  11. 11Refactor parallel lists and lookup-based lists into clearer, safer structures.

Sections at a Glance

Sections 20.1 – 20.2
Why Choice Matters and What the Tools Are
Design Questions · Lists · Tuples · Sets · Dictionaries

Data Structure Choice Matters

When you are new to programming, any structure that works feels like a good choice. If you can store ten names in a list, you use a list. If you have seen dictionaries solve a problem like this before, you try a dictionary. The program runs. You move on.

This approach has a real cost. Programs that use the wrong structure for their data become harder to read, harder to fix, and slower to run as they grow. A choice that works for ten items can become genuinely painful with ten thousand. A structure that stores data correctly can still make your code confusing if it hides the meaning of what you are tracking.

Good programmers treat data design as a real step in solving a problem. Before writing code, they ask a short set of questions:

  • What kind of data am I storing?
  • How will I need to access it?
  • Does order matter?
  • Are values allowed to repeat?
  • Will I need to look things up by name or by position?
  • Will this data change, or should it stay fixed?
  • How will I read this structure six weeks from now?

This chapter gives you a framework for answering these questions.

Study Note

None of the structures in this chapter are new. The skill being built here is judgment — the habit of comparing options deliberately instead of defaulting to whichever structure you used last time.

Reviewing the Four Main Collection Types

Before comparing structures, review what each one is.

List

A list is an ordered, mutable sequence. You can store any mix of values, access them by index, add items, remove items, and change items in place.

scores = [88, 72, 95, 61, 84]
scores.append(77)
scores[0] = 90

Lists are the most general-purpose collection. When you are not sure which structure to use, you will often reach for a list first.

Tuple

A tuple is an ordered, immutable sequence. Once created, it cannot be changed.

dimensions = (1920, 1080)
point = (3.5, -2.1)

Tuples signal that a group of values belongs together as a fixed unit. They are useful for coordinates, settings, records returned from a function, and any case where you want Python to prevent accidental modification.

Set

A set is an unordered collection of unique values. Duplicates are automatically discarded. Sets support fast membership testing and set operations like union and intersection.

seen_ids = {1001, 1002, 1003}
seen_ids.add(1004)
seen_ids.add(1001)   # Already there; set does not change
print(1002 in seen_ids)   # True

Sets are useful when you care about whether something exists, not where it is or how many times it appears.

Dictionary

A dictionary is an unordered collection of key-value pairs. You look up values by key, not by position. Keys must be unique and immutable.

student = {
    "name": "Amara",
    "grade": 10,
    "gpa": 3.7
}
print(student["name"])

Dictionaries are the natural structure for named records, lookup tables, and counting problems. They are the most powerful general-purpose mapping structure in Python.

Sections 20.3 – 20.6
Four Dimensions of Comparison
Order · Uniqueness · Lookup Method · Mutability

Ordered Versus Unordered Data

One of the first questions to ask about your data is whether order matters.

When order matters

  • A sequence of steps that must happen in a specific order.
  • A list of moves in a game, recorded as they happen.
  • A ranked leaderboard where first place is different from tenth place.
  • Lines of text in a file, which must stay in reading order.

For ordered data, use a list or a tuple. Lists are for ordered data that changes. Tuples are for ordered data that should stay fixed.

When order does not matter

  • A collection of student IDs that have submitted an assignment.
  • A set of unique tags applied to a document.
  • A lookup table that maps country codes to country names.
  • A counter that tracks how many times each word appears in a text.

For unordered data, use a set (if you only care about membership) or a dictionary (if each item has an associated value).

A common mistake

Many beginners store unordered data in lists and then write slow, complicated code to search through them.

Checking membership in a list — works, but slow for large lists
submitted_ids = [1001, 1002, 1003, 1004]
if 1002 in submitted_ids:
    print("Already submitted")
Checking membership in a set — faster and clearer
submitted_ids = {1001, 1002, 1003, 1004}
if 1002 in submitted_ids:
    print("Already submitted")

When you only need to know whether something is present and you never need to know where it falls in a sequence, a set is a better fit than a list.

Unique Versus Repeated Values

Ask yourself: does this data allow duplicates, or must every value appear at most once?

Duplicates are allowed and meaningful

A list of quiz scores for a class allows repeated values. Two students can both score 85. The duplicate values carry real information — two different students earned that score.

quiz_scores = [85, 72, 85, 90, 61, 72, 85]

Here a list is correct. A set would silently discard the duplicates, losing information.

Duplicates must be prevented

A collection of registered usernames should not allow the same username twice. A set of unique items sold today should not count the same item more than once in its membership.

registered_usernames = {"amara2024", "bento_k", "chen_li"}

A set enforces uniqueness automatically. You do not have to write extra code to prevent duplicates.

Duplicates should be counted

Sometimes duplicates carry information not as individual entries, but as counts.

Counting word frequencies
word_counts = {}
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]

for word in words:
    if word in word_counts:
        word_counts[word] += 1
    else:
        word_counts[word] = 1

print(word_counts)
# {'apple': 3, 'banana': 2, 'cherry': 1}

When duplicates are meaningful as frequencies, a dictionary with counts is the right structure.

Lookup by Position and Lookup by Key

A second major axis of comparison is how you access the data.

Position-based access

Lists and tuples let you access items by their index. Position has meaning. The first item is different from the third item because of where it sits.

temperatures = [72.1, 68.4, 75.0, 80.2, 79.9]
print(temperatures[0])   # First reading
print(temperatures[-1])  # Most recent reading

This works well when items are naturally ordered and position carries meaning: days of the week, steps in a process, readings taken in sequence.

Key-based access

Dictionaries let you access items by a meaningful name. You do not need to remember that the student's GPA is stored at index 2. You look it up by the key "gpa".

student = {"name": "Amara", "grade": 10, "gpa": 3.7}
print(student["gpa"])

Key-based access is clearer whenever the data has named fields. Compare:

Tuple version — you must remember which index means what
student = ("Amara", 10, 3.7)
print(student[2])   # What is index 2? You have to remember.
Dictionary version — the key is self-documenting
student = {"name": "Amara", "grade": 10, "gpa": 3.7}
print(student["gpa"])   # Clear

When names convey meaning more clearly than positions, use a dictionary.

Mutable and Immutable Data

Mutable data can be changed after it is created. Immutable data cannot.

StructureMutable?
ListYes
DictionaryYes
SetYes
TupleNo

Use immutability as a signal

Tuples are not just a less-capable version of lists. They communicate something: these values belong together and should not change.

A coordinate pair like (latitude, longitude) should be a tuple. You are not going to modify one component of a location mid-program. The pair is a single unit of fixed information.

An RGB color like (255, 128, 0) should be a tuple. Colors in a design palette do not change at runtime.

Configuration settings loaded once at startup can reasonably be stored in a tuple to prevent accidental modification.

WINDOW_SIZE = (1280, 720)      # Tuple: this should not change during the program
player_position = [640, 360]   # List: this changes constantly

Using tuples for fixed data and lists for changing data makes your intent visible in the code itself.

Sections 20.7 – 20.9
Building With the Tools
Records · Safe Combinations · Tradeoffs

Modeling Real-World Records

Many programming problems involve tracking records: things with multiple named properties. A student has a name, a grade level, and a GPA. A product has a name, a price, and a quantity in stock. A contact has a first name, a last name, a phone number, and an email address.

Python gives you two main ways to model a single record before you reach classes: a tuple or a dictionary.

Tuple as a record

student = ("Amara", 10, 3.7)

This works, but the meaning of each position is not visible in the code. You must remember the order: name first, grade second, GPA third. This is fragile. If you later add a field in the middle, every piece of code that unpacks the tuple may break.

Tuple records work best when the structure is small, stable, and well-known — like a coordinate or a date.

Dictionary as a record

student = {
    "name": "Amara",
    "grade": 10,
    "gpa": 3.7
}

A dictionary record is more readable. The keys document themselves. You can add a new field without breaking existing code that reads other fields. The cost is slightly more typing.

Rule of Thumb

For most beginner-to-intermediate programs, dictionary records are clearer and safer than tuple records unless the structure is very simple.

Combining Structures Safely

Real programs rarely use one structure in isolation. They combine them. The most common combinations are described below.

List of dictionaries — a table of records

students = [
    {"name": "Amara", "grade": 10, "gpa": 3.7},
    {"name": "Bento", "grade": 11, "gpa": 3.2},
    {"name": "Chen Li", "grade": 10, "gpa": 3.9}
]

for student in students:
    print(student["name"], student["gpa"])

This models a table where each row is a dictionary. It is one of the most useful patterns in practical Python.

Dictionary of lists — grouped items by category

class_roster = {
    "grade_10": ["Amara", "Chen Li", "Fatima"],
    "grade_11": ["Bento", "Kenji", "Lucia"]
}

print(class_roster["grade_10"])

This models groups. Each key names a category and maps to the list of items in that category.

Dictionary of dictionaries — nested records indexed by key

inventory = {
    "apple": {"price": 0.99, "quantity": 150},
    "banana": {"price": 0.59, "quantity": 80},
    "cherry": {"price": 3.49, "quantity": 40}
}

print(inventory["banana"]["price"])

This models a lookup table where each entry has multiple named properties.

Rules for combining safely

Design Rules
Four rules for combining structures without creating confusion
  • Rule 1Keep nesting shallow. If you need to go three or four levels deep to access a value, the structure is probably too complex. Consider using a class or breaking the data into separate, simpler structures.
  • Rule 2Use consistent shapes. Every dictionary in a list of dictionaries should have the same keys. Inconsistency makes iteration unpredictable and debugging painful.
  • Rule 3Name clearly. A variable named data tells you nothing. A variable named students, inventory, or word_counts tells you what it contains and makes the structure more readable.
  • Rule 4Write a small example before building the full structure. Before you process 500 records, build a version with 3 records and make sure your code works on the small case.

Data Structure Tradeoffs

No structure is best for everything. Every choice involves tradeoffs.

NeedBest fitWhy
Ordered sequence that changesListMutable, indexed, flexible
Ordered sequence that should not changeTupleImmutable, signals fixed data
Unique values, fast membership checkSetNo duplicates, fast in check
Named fields for a single recordDictionaryKeys are self-documenting
Counting occurrencesDictionary with integer valuesNatural accumulator pattern
Table of similar recordsList of dictionariesRows of uniform shape
Groups of items by categoryDictionary of listsCategory as key, items as list
Fast lookup by nameDictionaryO(1) average key lookup
Preserving insertion orderList or dictionary (Python 3.7+)Both preserve insertion order
Removing duplicates from a sequenceSetAutomatic deduplication
The Honest Answer

For most beginner Python programs, you will use lists and dictionaries for nearly everything. Lists handle sequences. Dictionaries handle records and lookups. Sets and tuples are valuable but more specialized.

The skill being built in this chapter is not memorizing a formula. It is the habit of pausing before you write code and asking: what shape does this data want to have?

Sections 20.10 – 20.11
Designing and Repairing Data
Sketching Before Coding · Recognizing and Fixing Weak Designs

Sketching Data Before Coding

One of the best habits a programmer can develop is sketching data structures on paper — or in a comment at the top of a file — before writing any code.

This sketch does not need to be formal. It just needs to answer: what does one piece of this data look like? What does a collection of it look like?

Example: planning a contact book

Suppose you are building a contact book that stores names, phone numbers, and email addresses. You want to look up a contact by name.

Sketch 1
List of tuples
  • Sketchcontacts = [("Amara Johnson", "555-1234", "[email protected]"), ("Bento Kowalski", "555-5678", "[email protected]")]
  • ProblemTo look up Amara, you would have to loop through the entire list comparing names. That is slow and cumbersome for a lookup task.
Sketch 2
List of dictionaries
  • Sketchcontacts = [{"name": "Amara Johnson", "phone": "555-1234", "email": "[email protected]"}, {"name": "Bento Kowalski", "phone": "555-5678", "email": "[email protected]"}]
  • Better, butEach record is self-documenting. But lookup by name still requires looping.
Sketch 3
Dictionary of dictionaries, keyed by name
  • Sketchcontacts = {"Amara Johnson": {"phone": "555-1234", "email": "[email protected]"}, "Bento Kowalski": {"phone": "555-5678", "email": "[email protected]"}}
  • ResultNow lookup by name is direct: print(contacts["Amara Johnson"]["phone"])

You would arrive at Sketch 3 faster if you asked the right questions before writing code:

  • How will I look up contacts? By name.
  • Then name should be the key.
  • What information goes with each name? Phone and email.
  • Then each entry should be a dictionary with those two fields.

The sketch is not the program. It is the plan that makes the program obvious.

Refactoring Data Structures

Sometimes you build a program with one structure and later discover it is the wrong choice. This is normal. The ability to recognize a weak design and improve it is a real programming skill.

Signs that a data structure needs rethinking

  • You loop through a list every time you need to find something by name. A dictionary would make lookup direct.
  • You use a list but never use indexes — you only check if item in list. A set would be more appropriate.
  • You use parallel lists: one for names, one for scores, one for grades. A list of dictionaries would keep related data together.
  • You use a dictionary but keep forgetting which key holds which value because the keys are not descriptive. Rename them.
  • Your nested structure requires three levels of indexing to get to a single value. Flatten or simplify.

Refactoring parallel lists into a list of dictionaries

Before
names  = ["Amara", "Bento", "Chen Li"]
scores = [88, 72, 95]
grades = ["B", "C", "A"]
The Problem

These three lists are coupled. If you sort scores, names and grades go out of sync unless you are careful.

After
students = [
    {"name": "Amara",   "score": 88, "grade": "B"},
    {"name": "Bento",   "score": 72, "grade": "C"},
    {"name": "Chen Li", "score": 95, "grade": "A"}
]

Now each student's data travels together. Sorting by score keeps name and grade attached to the correct student.

Refactoring a lookup list into a dictionary

Before
product_names  = ["apple", "banana", "cherry"]
product_prices = [0.99, 0.59, 3.49]

# To find the price of banana:
index = product_names.index("banana")
price = product_prices[index]
After
prices = {"apple": 0.99, "banana": 0.59, "cherry": 3.49}
price = prices["banana"]

The second version is shorter, clearer, and faster.

Chapter Summary

In this chapter you learned how to evaluate data structure choices rather than simply accepting the first structure that comes to mind.

Key Terms

mutable
A data structure that can be changed after it is created. Lists, dictionaries, and sets are mutable.
immutable
A data structure that cannot be changed after it is created. Strings and tuples are immutable.
ordered
A collection where the position of each item is meaningful and preserved. Lists and tuples are ordered.
unordered
A collection where items have no defined position. Sets and dictionaries (before Python 3.7) are unordered. Python 3.7+ dictionaries preserve insertion order but are not typically used for position-based access.
unique
A collection that does not allow duplicate values. Sets enforce uniqueness automatically.
key-value pair
A single entry in a dictionary, consisting of a key used for lookup and an associated value.
record
A collection of named fields describing a single object or entity. Dictionaries are commonly used as records in Python.
list of dictionaries
A list in which each element is a dictionary with the same set of keys, modeling a table of records.
dictionary of lists
A dictionary in which each value is a list, modeling groups of items organized by category.
parallel lists
Multiple lists used together so that items at the same index are related. Generally a design smell; lists of dictionaries are usually cleaner.
refactoring
Restructuring existing code or data to improve clarity, organization, or efficiency without changing what the program does.
data design
The process of choosing and sketching appropriate data structures before writing code.

Code Reading

Read each snippet carefully. Answer the questions without running the code.

Exercise 1
seen = set()
items = ["apple", "banana", "apple", "cherry", "banana", "apple"]

for item in items:
    seen.add(item)

print(len(seen))
  1. What does seen contain after the loop?
  2. What does len(seen) print?
  3. Why does this produce a different result than len(items)?
Exercise 2
catalog = {
    "pen":      {"price": 1.25, "stock": 200},
    "notebook": {"price": 4.50, "stock": 85},
    "ruler":    {"price": 0.99, "stock": 300}
}

for item, details in catalog.items():
    if details["stock"] < 100:
        print(item, "is low on stock")
  1. Which item or items will be printed?
  2. What would you change to print items with fewer than 150 in stock instead?
  3. How would you access the price of a ruler without the loop?
Exercise 3
records = [
    {"name": "Amara", "score": 88},
    {"name": "Bento", "score": 72},
    {"name": "Chen Li", "score": 95}
]

total = 0
for r in records:
    total += r["score"]

average = total / len(records)
print(f"Average score: {average:.1f}")
  1. What does this program print?
  2. What would happen if records were an empty list? Would the program crash, or produce a wrong answer?
  3. How would you modify the loop to also print each student's name and score?

Code Tracing

Exercise 4
Trace the following program step by step
inventory = {}

items = [("apple", 5), ("banana", 3), ("apple", 2), ("cherry", 7), ("banana", 1)]

for name, qty in items:
    if name in inventory:
        inventory[name] += qty
    else:
        inventory[name] = qty

print(inventory)
After iterationinventory
1 
2 
3 
4 
5 

What does the program print?

Exercise 5
groups = {}
students = [
    ("Amara",   "A"),
    ("Bento",   "B"),
    ("Chen Li", "A"),
    ("Fatima",  "C"),
    ("Kenji",   "B"),
    ("Lucia",   "A")
]

for name, grade in students:
    if grade not in groups:
        groups[grade] = []
    groups[grade].append(name)

for grade in sorted(groups):
    print(grade, groups[grade])

Trace the state of groups after each iteration of the first loop. Then write exactly what the program prints.

Predict the Output

Predict what each snippet prints, or describe what error it produces.

Exercise 6
colors = {"red", "blue", "green", "red", "blue"}
print(len(colors))
print("purple" in colors)
Exercise 7
point = (4, 7)
point[0] = 10
Exercise 8
data = {"x": [1, 2, 3], "y": [4, 5, 6]}
data["x"].append(99)
print(data)
Exercise 9
words = ["the", "quick", "the", "brown", "fox", "the"]
unique = set(words)
print(sorted(unique))

Debugging and Error Analysis

Each program below contains a problem. Identify the error and explain how to fix it.

Exercise 10
# Goal: store each student's scores
records = {}
records["Amara"] = 88
records["Bento"] = 72
records["Amara"] = 95   # Amara took the quiz again

print(records["Amara"])  # Should print both 88 and 95

What is wrong with this design? What structure would let you store multiple scores per student?

Exercise 11
contacts = [
    ("Amara", "555-1234"),
    ("Bento", "555-5678")
]

print(contacts["Amara"])

What error does this produce? Explain why, and suggest a better data structure for this use case.

Exercise 12
tags = ["python", "beginner", "python", "tutorial", "beginner"]
unique_tags = set(tags)
unique_tags[0]   # Get the first tag

What is wrong with this code? What should you do instead if you need an ordered, deduplicated list of tags?

Exercise 13
students = [
    {"name": "Amara", "score": 88},
    {"name": "Bento", "score": 72},
    {"name": "Chen Li"}   # Missing "score" key
]

for s in students:
    print(s["name"], s["score"])

What error will this produce and when? How would you guard against it?

PracticeStructure Selection Practice

For each situation, state which data structure you would use and explain why in one or two sentences.

  1. You are storing the names of all cities visited on a road trip, in the order they were visited.
  2. You are storing a set of student ID numbers who have completed a survey. You only need to check whether a given ID is in the set.
  3. You are storing a phone directory where each person's name maps to their phone number.
  4. You are storing a fixed RGB color used in a drawing program: red = 255, green = 128, blue = 0.
  5. You are storing a collection of product listings. Each product has a name, a price, and a quantity in stock.
  6. You are counting how many times each letter appears in a string.
  7. You are storing the contents of a tic-tac-toe board as a 3×3 grid of values.
  8. You are storing the list of tags on a blog post. Tags must be unique, but you do not care about their order.
PracticeData Modeling Scenarios
Exercise 14
Gradebook design

You are building a gradebook for a class of students. Each student has a first name and last name, a student ID number, a list of assignment scores, and a final letter grade.

  1. Sketch a data structure that represents one student.
  2. Sketch a data structure that represents the entire class.
  3. Write the Python code for a small example with two students and two scores each.
  4. Write a loop that prints each student's name and their average score.
Exercise 15
Inventory system design

You are building an inventory system for a small store. The store sells several products. Each product has a name, a price, and a quantity on hand. You need to look up a product's price by name, reduce the quantity when a product is sold, and print a report of all products with fewer than 10 units remaining.

  1. What structure would you use? Sketch it.
  2. Write the Python code for a small example with three products.
  3. Write a function that takes the inventory and a product name and reduces that product's quantity by 1. Handle the case where the product is not found.
  4. Write a loop that prints the low-stock report.

Code Review Practice

Exercise 16
Working code with room to improve
names   = ["Amara", "Bento", "Chen Li", "Fatima"]
scores  = [88, 72, 95, 61]
passing = [True, True, True, False]

for i in range(len(names)):
    if passing[i]:
        print(names[i], "passed with", scores[i])
    else:
        print(names[i], "did not pass")

This program works correctly. Identify at least two design improvements and explain each one.

Exercise 17
A fragile contact record
# Each contact stored as: (first_name, last_name, phone, email, city)
contacts = [
    ("Amara", "Johnson", "555-1234", "[email protected]", "Springfield"),
    ("Bento", "Kowalski", "555-5678", "[email protected]", "Riverside")
]

# Print each contact's email
for c in contacts:
    print(c[3])

Identify what is fragile about the data design and suggest a safer alternative.

PracticeShort Programming Tasks
Task 1
count_votes

Write a function called count_votes that takes a list of strings representing votes (for example ["yes", "no", "yes", "yes", "no", "abstain"]) and returns a dictionary mapping each option to its vote count.

def count_votes(votes):
    # Your code here
    pass

results = count_votes(["yes", "no", "yes", "yes", "no", "abstain"])
print(results)
# Expected: {'yes': 3, 'no': 2, 'abstain': 1}
Task 2
group_by_grade

Write a function called group_by_grade that takes a list of student dictionaries (each with "name" and "grade" keys) and returns a dictionary mapping each grade to a list of student names.

students = [
    {"name": "Amara",   "grade": "A"},
    {"name": "Bento",   "grade": "B"},
    {"name": "Chen Li", "grade": "A"},
    {"name": "Fatima",  "grade": "C"},
    {"name": "Kenji",   "grade": "B"}
]

def group_by_grade(students):
    # Your code here
    pass

print(group_by_grade(students))
# Expected: {'A': ['Amara', 'Chen Li'], 'B': ['Bento', 'Kenji'], 'C': ['Fatima']}
Task 3
remove_duplicates

Write a function called remove_duplicates that takes a list and returns a new list with duplicates removed, preserving the original order of first appearances.

def remove_duplicates(items):
    # Your code here
    pass

print(remove_duplicates(["apple", "banana", "apple", "cherry", "banana"]))
# Expected: ['apple', 'banana', 'cherry']
Hint

A set can track which items you have already seen.

Task 4
Refactor parallel lists

Refactor the following program to use a list of dictionaries instead of parallel lists.

product_names  = ["pen", "notebook", "ruler", "eraser"]
product_prices = [1.25, 4.50, 0.99, 0.75]
product_stock  = [200, 85, 300, 150]

for i in range(len(product_names)):
    print(product_names[i], "$" + str(product_prices[i]), "qty:", product_stock[i])

After refactoring, add a loop that prints only the products priced under $2.00.

Task 5
find_common

Write a function called find_common that takes two lists and returns a set of values that appear in both lists.

def find_common(list_a, list_b):
    # Your code here
    pass

a = [1, 2, 3, 4, 5]
b = [3, 4, 5, 6, 7]
print(find_common(a, b))
# Expected: {3, 4, 5}
Mixed ReviewMixed Data Structure Review

These questions draw on material from Chapters 16 through 20.

  1. What does sorted({"banana", "apple", "cherry"}) return? What type does it return?
  2. What is the difference between list.append(item) and list.extend(other_list)?
  3. Can you use a list as a dictionary key? Can you use a tuple? Explain why.
  4. What does dict.get("key", default) do that dict["key"] does not?
  5. Write a one-line expression that creates a set of unique characters in the string "mississippi".
  6. What happens when you iterate over a dictionary with a for loop? What does the loop variable contain?
  7. Write code that adds 1 to inventory["apple"] only if "apple" is already a key in inventory. Do not raise a KeyError if it is not present.
Mini-ProjectInventory Tracker

Build a console-based inventory tracker that lets the user manage a small store's stock. The program should store inventory as a dictionary of dictionaries.

Requirements

  • AddAdd a product. The user enters a product name, price, and quantity. If the product already exists, print an error message instead of overwriting it.
  • RestockRestock a product. The user enters a product name and a quantity to add. Update the quantity in stock.
  • SellSell a product. The user enters a product name and quantity sold. Reduce the stock. If the requested quantity exceeds current stock, print an error and do not update.
  • ViewView all inventory. Print a formatted table showing each product name, price, and quantity.
  • ReportLow stock report. Print all products where quantity is below a threshold (you choose: 10 is a reasonable default).
  • QuitEnd the program cleanly.

Suggested data shape

inventory = {
    "apple":    {"price": 0.99, "quantity": 150},
    "banana":   {"price": 0.59, "quantity":  80},
    "cherry":   {"price": 3.49, "quantity":   8}
}

Starting point

def add_product(inventory, name, price, quantity):
    pass

def restock(inventory, name, amount):
    pass

def sell(inventory, name, amount):
    pass

def view_inventory(inventory):
    pass

def low_stock_report(inventory, threshold=10):
    pass

def main():
    inventory = {}
    running = True
    while running:
        print("\n--- Inventory Tracker ---")
        print("1. Add product")
        print("2. Restock product")
        print("3. Sell product")
        print("4. View all inventory")
        print("5. Low stock report")
        print("6. Quit")
        choice = input("Choice: ")
        # Your menu logic here

main()

Extension challenges

  • Allow the user to remove a product from inventory entirely.
  • Track the total value of inventory (sum of price × quantity for all products).
  • Save the inventory to a text file when the user quits, and reload it on startup.
ReviewReview Questions
  1. Name the four main built-in collection types in Python. For each one, state whether it is ordered, mutable, and whether it allows duplicate values.
  2. You are building a program to track which students have handed in an assignment. You only need to check whether a given student has submitted. Which structure would you use, and why?
  3. Explain the difference between accessing data by position and accessing data by key. Give an example of each.
  4. What is a parallel list design? Give an example. What is the main problem with parallel lists, and what structure is usually better?
  5. A friend says: "I always use lists because they work for everything." Is this true? In what situations would a set or dictionary be a significantly better choice? Give one example for each.
  6. What does it mean to say that a tuple is immutable? Give one example of data that should be stored as a tuple rather than a list.
  7. You have a list of dictionaries. Every dictionary has a "name" key and a "score" key. You want to find the dictionary with the highest score. Write the logic, in plain English, for how you would do this. Then write it in Python.
  8. Explain what refactoring a data structure means. Give a concrete before-and-after example.
  9. Why is it useful to sketch a data structure before writing code? What questions should a sketch answer?
  10. You have the following structure. Write code that prints the email address of every contact in the dictionary.
    contacts = {
        "Amara Johnson":  {"phone": "555-1234", "email": "[email protected]"},
        "Bento Kowalski": {"phone": "555-5678", "email": "[email protected]"},
        "Chen Li":        {"phone": "555-9012", "email": "[email protected]"}
    }
Chapter 20 Checklist
Use this to confirm your understanding before moving to Chapter 21
Concepts
  • I can describe the four main collection types and their defining properties.
  • I can decide whether order matters for a given problem.
  • I can decide whether duplicates should be allowed, prevented, or counted.
  • I can explain the difference between position-based and key-based access.
  • I can explain the difference between mutable and immutable structures.
  • I can model a record using either a tuple or a dictionary and justify the choice.
Applications
  • I can combine structures using list of dictionaries, dictionary of lists, and dictionary of dictionaries.
  • I can apply the four rules for combining structures safely.
  • I can sketch a data structure on paper before writing code.
  • I can recognize parallel lists and lookup loops as signs a structure needs to change.
  • I can refactor parallel lists into a list of dictionaries.
  • I can refactor a lookup list into a dictionary.
Study Habits
  • I pause and ask design questions before writing code, rather than defaulting to the first structure that comes to mind.
  • I can explain my data structure choice to someone else in one or two sentences.
Preview — Chapter 21
Files, Paths, and Text Data

Chapter 20 completes Part IV by teaching you to choose and combine data structures deliberately. Chapter 21 begins Part V: Files, Data, and Practical Automation. So far, every program you have written starts fresh when it runs and forgets everything when it ends. Files change that. Chapter 21 will teach you how to read data from text files, write data back to files, append to existing files, and handle the file path and file error problems that come with working outside the program's memory. You will build programs that can remember information between sessions — a skill that makes programs far more useful in the real world.

MGU Library Connection
  • Programming with Python — Appendix H: Data Structure Reference
  • Programming with Python — Appendix C: Python Syntax Quick Reference
  • Companion resource: Data Structure Selection Guide
  • Companion resource: Dictionary Methods Chart
  • Companion resource: List Methods Chart
  • Next chapter: Files, Paths, and Text Data (Chapter 21)