Skip to content

A3: Music Database

Prerequisites: list comprehensions, map, filter, reduce

Learning Objective: Practice choosing the right functional tool for a task — searching a collection (filter), reshaping it (map or a comprehension), and summarizing it down to one value (reduce) — instead of reaching for a for loop every time.

A “database,” for this project, is nothing more exotic than a list of dictionaries, one per song. Here’s a small sample to develop and test against:

songs = [
{"artist": "Miles Davis", "title": "So What", "genre": "jazz", "year": 1959, "duration": 545, "plays": 812},
{"artist": "Miles Davis", "title": "Blue in Green", "genre": "jazz", "year": 1959, "duration": 337, "plays": 405},
{"artist": "Nina Simone", "title": "Feeling Good", "genre": "jazz", "year": 1965, "duration": 176, "plays": 990},
{"artist": "Daft Punk", "title": "One More Time", "genre": "dance", "year": 2000, "duration": 320, "plays": 1500},
{"artist": "Daft Punk", "title": "Digital Love", "genre": "dance", "year": 2001, "duration": 300, "plays": 640},
{"artist": "Fleetwood Mac", "title": "Dreams", "genre": "rock", "year": 1977, "duration": 257, "plays": 1230},
{"artist": "Fleetwood Mac", "title": "The Chain", "genre": "rock", "year": 1977, "duration": 271, "plays": 700},
{"artist": "Bill Withers", "title": "Ain't No Sunshine", "genre": "soul", "year": 1971, "duration": 125, "plays": 980},
{"artist": "Aretha Franklin", "title": "Respect", "genre": "soul", "year": 1967, "duration": 147, "plays": 1100},
{"artist": "Kraftwerk", "title": "Autobahn", "genre": "dance", "year": 1974, "duration": 1445, "plays": 210},
]

(duration is in seconds; plays is a play count.) You’re welcome to extend this list with more songs, including ones you like, as long as you keep the same fields.

Write each of the following as a single expression built from filter, map, reduce, or a list comprehension — not a for loop with an accumulator. Part of the point of this project is to get comfortable reaching for the right tool; if you find yourself writing result = [] followed by a loop that appends to it, stop and look for the comprehension or function that does the same job in one line.

  1. songs_by_artist(songs, artist) — return the list of song dictionaries by a given artist. (filter, or a comprehension.)
  2. songs_by_genre_after_year(songs, genre, year) — return songs matching a genre and released after a given year. (This is a good candidate for a comprehension with two conditions, or an and inside a filter predicate.)
  3. titles(songs) — return just the list of titles from a list of song dictionaries. (map, or a comprehension.)
  4. display_strings(songs) — return strings formatted like "Dreams — Fleetwood Mac (1977)" for each song. (map with a helper function, or a comprehension.)
  5. total_duration(songs) — return the total duration, in seconds, of a list of songs. (reduce, or Python’s built-in sum on a comprehension — try it both ways.)
  6. most_played(songs) — return the single song dictionary with the highest play count. (reduce with a two-argument “pick the better one” function, or Python’s built-in max with a key=.)
  7. search(songs, genre, min_year): combine ideas 2 and 4 — return the display strings (not the raw dictionaries) for every song in genre released in min_year or later, using a single comprehension that both filters and transforms.

For each function, write a couple of lines of test code that calls it on the sample songs list above and prints (or asserts) a result you’ve checked by hand.

At the top of your main Python file, add a comment that answers these questions:

  • About how long did the assignment take you?
  • What was hard?
  • What was easy?
  • Was anything in the assignment unclear?

We use your answers to improve the assignment.

  • Each function uses the functional tool suited to what it’s doing (search → filter/comprehension, reshape → map/comprehension, summarize → reduce/sum/max) rather than a hand-rolled accumulator loop.
  • most_played and total_duration handle an empty list sensibly (decide what that should mean, and say so in a comment). Hint: Both max() and reduce() accept an optional argument for exactly this situation.
  • Test code demonstrates each function against the sample data, with results you’ve verified by hand.