Exercises: Lists and Tuples

These exercises progress from foundational list operations through advanced list comprehensions, aliasing puzzles, and practical applications. Every exercise that asks you to write code expects a complete, runnable program.

Difficulty Guide: - ⭐ Foundational (5-10 min each) - ⭐⭐ Intermediate (10-20 min each) - ⭐⭐⭐ Challenging (20-40 min each) - ⭐⭐⭐⭐ Advanced/Research (40+ min each)


Part A: List Basics ⭐

A.1. Create a list of your five favorite foods. Print the first item, the last item, and the middle item using indexing. Then print the list in reverse using slicing.

A.2. Given the list numbers = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100], write code that prints: - a) The first three elements - b) The last four elements - c) Every third element - d) Elements from index 2 to index 7

A.3. Predict the output of each line without running the code, then verify:

fruits = ["apple", "banana", "cherry", "date"]
print(len(fruits))
print(fruits[-2])
print(fruits[1:3])
print("banana" in fruits)
print("grape" not in fruits)

A.4. Write a program that asks the user to enter 5 numbers (one at a time), stores them in a list, and then prints the list, the sum, and the average.

A.5. Explain the difference between append() and extend() with a code example that demonstrates both. What happens if you append a list to a list?

A.6. Given this code, predict what items will contain after each line:

items = ["a", "b", "c", "d", "e"]
items.pop(2)
items.insert(1, "x")
items.remove("d")
items.append("z")

Part B: List Methods and Iteration ⭐⭐

B.1. Write a function stats(numbers) that takes a list of numbers and returns a tuple of (minimum, maximum, average, count). Test it with the list [23, 45, 12, 67, 34, 89, 56].

Expected output:

Min: 12, Max: 89, Average: 46.57, Count: 7

B.2. Write a program that creates a list of 10 random integers between 1 and 100 (use random.randint). Then: - a) Print the original list - b) Print the sorted list (without modifying the original) - c) Print the original list again to prove it wasn't modified - d) Sort it in place and print the result

B.3. Using enumerate(), write a function find_all_indices(lst, target) that returns a list of all indices where target appears.

# Example:
find_all_indices([3, 1, 4, 1, 5, 1], 1)  # Should return [1, 3, 5]
find_all_indices([3, 1, 4, 1, 5, 1], 9)  # Should return []

B.4. Using zip(), write a program that takes two lists — names and scores — and prints a formatted report. If the lists have different lengths, print a warning.

names = ["Alice", "Bob", "Charlie", "Diana"]
scores = [92, 85, 78, 95]

Expected output:

Student Report
--------------
Alice:    92
Bob:      85
Charlie:  78
Diana:    95

B.5. Write a function rotate_left(lst, n) that rotates a list n positions to the left using slicing. The function should return a new list, not modify the original.

rotate_left([1, 2, 3, 4, 5], 2)  # [3, 4, 5, 1, 2]
rotate_left([1, 2, 3, 4, 5], 0)  # [1, 2, 3, 4, 5]
rotate_left([1, 2, 3, 4, 5], 5)  # [1, 2, 3, 4, 5]

B.6. Write a function interleave(list1, list2) that combines two lists by alternating elements. If one list is longer, append the remaining elements at the end.

interleave([1, 2, 3], ["a", "b", "c"])        # [1, 'a', 2, 'b', 3, 'c']
interleave([1, 2], ["a", "b", "c", "d"])       # [1, 'a', 2, 'b', 'c', 'd']

Part C: List Comprehensions ⭐⭐

C.1. Rewrite each of the following loops as a list comprehension:

# a)
result = []
for x in range(1, 11):
    result.append(x ** 3)

# b)
result = []
for word in ["hello", "world", "python"]:
    result.append(len(word))

# c)
result = []
for n in range(1, 21):
    if n % 3 == 0:
        result.append(n)

C.2. Write a list comprehension that takes a list of strings and returns only those that have more than 5 characters, converted to title case.

words = ["apple", "hi", "banana", "cat", "elephant", "dog", "watermelon"]
# Expected: ['Banana', 'Elephant', 'Watermelon']

C.3. Given a list of temperatures in Fahrenheit, write a comprehension that converts them to Celsius (formula: (F - 32) * 5/9) and rounds to one decimal place. Only include temperatures where the Celsius result is positive.

temps_f = [32, 50, 72, 95, 14, 100, 68, 28]
# Expected: [10.0, 22.2, 35.0, 37.8, 20.0]

C.4. Write a comprehension that flattens a list of lists of integers and keeps only even numbers:

nested = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
# Expected: [2, 4, 6, 8, 10, 12]

C.5. A common interview question: given a list of integers, use a comprehension to create a list where each element is replaced by the string "FizzBuzz" if divisible by both 3 and 5, "Fizz" if divisible by 3, "Buzz" if divisible by 5, or the number itself otherwise. Test with range(1, 16).


Part D: Tuples and Unpacking ⭐⭐

D.1. Create a list of tuples representing books: (title, author, year, pages). Include at least 5 books. Then write code to: - a) Print all books published after 2000 - b) Find the book with the most pages - c) Calculate the average page count

D.2. Write a function split_names(full_names) that takes a list of full names (strings) and returns two separate lists: first names and last names. Use tuple unpacking.

names = ["Alice Smith", "Bob Jones", "Charlie Brown"]
firsts, lasts = split_names(names)
# firsts: ['Alice', 'Bob', 'Charlie']
# lasts: ['Smith', 'Jones', 'Brown']

D.3. Write a function that takes a list of (student, score) tuples and returns three lists: students who got A (90+), B (80-89), and C (70-79). Use tuple unpacking in your loop.

results = [("Alice", 92), ("Bob", 85), ("Charlie", 71), ("Diana", 95), ("Eve", 88)]
a_students, b_students, c_students = grade_groups(results)
# a_students: ['Alice', 'Diana']
# b_students: ['Bob', 'Eve']
# c_students: ['Charlie']

D.4. Use star unpacking to write a function head_tail(lst) that returns the first element and a list of the remaining elements:

first, rest = head_tail([10, 20, 30, 40, 50])
# first: 10
# rest: [20, 30, 40, 50]

Part E: Aliasing and Copying ⭐⭐⭐

E.1. Predict the output of this code. Draw the memory diagram for each step. Then run it to verify.

a = [1, 2, 3]
b = a
c = a.copy()
a.append(4)
b.append(5)
c.append(6)
print(a)
print(b)
print(c)

E.2. This function has an aliasing bug. Find it, explain it, and fix it:

def add_greeting(names, greeting="Hello"):
    result = names
    for i in range(len(result)):
        result[i] = f"{greeting}, {result[i]}!"
    return result

original = ["Alice", "Bob", "Charlie"]
greeted = add_greeting(original)
print(greeted)
print(original)  # Surprise! This isn't what you expect.

E.3. Explain why this code produces unexpected output, and fix it:

grid = [[0] * 4] * 3
grid[0][0] = 1
print(grid)
# Expected: [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
# Actual:   [[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]

E.4. Write a function safe_append(original, item) that appends an item to a copy of the list and returns the new list, leaving the original unchanged. Include a test that proves the original is unmodified.


Part F: Nested Lists and 2D Data ⭐⭐⭐

F.1. Write a function transpose(matrix) that transposes a 2D list (rows become columns).

matrix = [
    [1, 2, 3],
    [4, 5, 6],
]
# transpose(matrix) should return:
# [[1, 4], [2, 5], [3, 6]]

F.2. Create a 5x5 multiplication table as a nested list. Then print it in a formatted grid:

  1   2   3   4   5
  2   4   6   8  10
  3   6   9  12  15
  4   8  12  16  20
  5  10  15  20  25

F.3. Write a tic-tac-toe board checker. Given a 3x3 nested list, write a function check_winner(board) that returns "X", "O", or None depending on whether there's a winner (three in a row horizontally, vertically, or diagonally).

board = [
    ["X", "O", "X"],
    ["O", "X", "O"],
    ["O", "X", "X"],
]
print(check_winner(board))  # "X" (diagonal)

Part G: Integrated Challenges ⭐⭐⭐⭐

G.1. Text Adventure Inventory System

Write an inventory system for a text adventure game. The player should be able to: - Pick up items (add to inventory list) - Drop items (remove from inventory) - Use items (remove and print an effect) - View inventory (print numbered list) - Check if they have a specific item

Include at least 5 commands and test with a sequence of actions. Items should be stored as tuples of (name, description, weight). The inventory has a maximum weight capacity of 50.

G.2. Grade Analytics

Write a complete grade analysis program. Given a list of (student_name, [scores]) tuples:

students = [
    ("Alice", [92, 85, 88, 95, 91]),
    ("Bob", [78, 82, 90, 65, 72]),
    ("Charlie", [95, 91, 87, 93, 98]),
    ("Diana", [60, 55, 72, 68, 63]),
]

Calculate and display: - a) Each student's average, min, and max score - b) The class average across all students - c) The student with the highest overall average - d) Each student's scores sorted from lowest to highest - e) A histogram showing how many students fall in each grade range (A, B, C, D, F)

G.3. Data Pipeline

Elena needs to process a list of donation records (tuples) and produce a summary report:

donations = [
    ("2024-01-15", "Alice", 100.00, "general"),
    ("2024-01-16", "Bob", 250.00, "education"),
    ("2024-01-17", "Charlie", 50.00, "general"),
    ("2024-01-18", "Alice", 75.00, "health"),
    ("2024-01-19", "Diana", 500.00, "education"),
    ("2024-01-20", "Bob", 150.00, "general"),
    ("2024-01-21", "Eve", 200.00, "health"),
    ("2024-01-22", "Alice", 300.00, "education"),
]

Write functions that: - a) Calculate total donations per category - b) Find the top donor (highest total across all donations) - c) List all unique donors, sorted alphabetically - d) Filter donations above a given threshold - e) Create a summary report that prints all of the above


Reflection Questions

After completing the exercises, answer these in a few sentences each:

  1. When would you choose a tuple over a list? Give two concrete examples.
  2. A junior developer writes backup = important_data before modifying important_data. Why doesn't this protect the original? What should they write instead?
  3. When is a list comprehension better than a for loop? When is a for loop better?