S3Q1 · Book Reading List Data Analysis¶
⚡ Quick Reference
Function: book_analysis(data: list, request: str)
def book_analysis(data: list, request: str):
n = len(data)
avg_r = sum(b["rating"] for b in data) / n
avg_p = sum(b["pages"] for b in data) / n
if request == "average_rating":
return round(avg_r, 2)
if request == "average_pages":
return round(avg_p, 2)
if request == "longest_book":
return max(data, key=lambda b: (b["pages"], b["rating"]))["book_id"]
if request == "above_average_books":
return {b["book_id"] for b in data
if b["rating"] > avg_r and b["pages"] > avg_p}
Key rules:
- Compute both averages upfront - reused by longest_book and above_average_books
- longest_book ties → highest rating wins (tuple key)
- above_average_books uses strict > for both conditions - returns a set
Problem Statement¶
Problem
Implement book_analysis(data, request) - four analytics operations on a list of book dicts.
Sample data:
data = [
{"book_id": "B1", "pages": 320, "rating": 4.5},
{"book_id": "B2", "pages": 150, "rating": 3.8},
{"book_id": "B3", "pages": 480, "rating": 4.2},
{"book_id": "B4", "pages": 480, "rating": 4.7},
{"book_id": "B5", "pages": 200, "rating": 4.9},
]
Operation 1 - average_rating¶
From sample: (4.5+3.8+4.2+4.7+4.9) / 5 = 22.1/5 = 4.42
Operation 2 - average_pages¶
From sample: (320+150+480+480+200) / 5 = 1630/5 = 326.0
Operation 3 - longest_book¶
Highest page count; ties broken by highest rating:
From sample: B3 and B4 both have 480 pages. B4 rating=4.7 > B3 rating=4.2 → "B4"
Operation 4 - above_average_books¶
Books with rating > avg_rating AND pages > avg_pages (strict):
avg_r = sum(b["rating"] for b in data) / len(data) # 4.42
avg_p = sum(b["pages"] for b in data) / len(data) # 326.0
return {b["book_id"] for b in data
if b["rating"] > avg_r and b["pages"] > avg_p}
From sample: - B1: rating 4.5 > 4.42 ✅, pages 320 > 326 ❌ - B2: rating 3.8 > 4.42 ❌ - B3: rating 4.2 > 4.42 ❌ - B4: rating 4.7 > 4.42 ✅, pages 480 > 326 ✅ → included - B5: rating 4.9 > 4.42 ✅, pages 200 > 326 ❌
Result: {"B4"}
Complete solution approaches¶
def book_analysis(data: list, request: str):
n = len(data)
avg_r = sum(b["rating"] for b in data) / n
avg_p = sum(b["pages"] for b in data) / n
if request == "average_rating":
return round(avg_r, 2)
if request == "average_pages":
return round(avg_p, 2)
if request == "longest_book":
return max(data, key=lambda b: (b["pages"], b["rating"]))["book_id"]
if request == "above_average_books":
return {b["book_id"] for b in data
if b["rating"] > avg_r and b["pages"] > avg_p}
def book_analysis(data: list, request: str):
n = len(data)
if request == "average_rating":
return round(sum(b["rating"] for b in data) / n, 2)
if request == "average_pages":
return round(sum(b["pages"] for b in data) / n, 2)
if request == "longest_book":
best = data[0]
for b in data[1:]:
if b["pages"] > best["pages"]:
best = b
elif b["pages"] == best["pages"] and b["rating"] > best["rating"]:
best = b
return best["book_id"]
if request == "above_average_books":
avg_r = sum(b["rating"] for b in data) / n
avg_p = sum(b["pages"] for b in data) / n
return {b["book_id"] for b in data
if b["rating"] > avg_r and b["pages"] > avg_p}
def book_analysis(data: list, request: str):
n = len(data)
avg_r = sum(b["rating"] for b in data) / n
avg_p = sum(b["pages"] for b in data) / n
ops = {
"average_rating": lambda: round(avg_r, 2),
"average_pages": lambda: round(avg_p, 2),
"longest_book": lambda: max(data, key=lambda b: (b["pages"], b["rating"]))["book_id"],
"above_average_books":lambda: {b["book_id"] for b in data
if b["rating"] > avg_r and b["pages"] > avg_p},
}
return ops[request]()
Key takeaways¶
Compute averages upfront - three tasks need them
avg_r and avg_p are used by both average_rating, average_pages, and above_average_books. Computing them once at the top avoids redundant scans and keeps each branch clean.
Tuple key for longest_book tie-breaking
key=lambda b: (b["pages"], b["rating"]) - max compares pages first; only when tied does rating decide. One max() call handles both criteria without extra logic.
above_average_books uses strict > and returns a set
Both conditions are strict greater-than - books exactly at the average are excluded. The return type is a set (use a set comprehension {...}), not a list.