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.
The database
Section titled “The database”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.
What to do
Section titled “What to do”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.
songs_by_artist(songs, artist)— return the list of song dictionaries by a given artist. (filter, or a comprehension.)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 anandinside afilterpredicate.)titles(songs)— return just the list of titles from a list of song dictionaries. (map, or a comprehension.)display_strings(songs)— return strings formatted like"Dreams — Fleetwood Mac (1977)"for each song. (mapwith a helper function, or a comprehension.)total_duration(songs)— return the total duration, in seconds, of a list of songs. (reduce, or Python’s built-insumon a comprehension — try it both ways.)most_played(songs)— return the single song dictionary with the highest play count. (reducewith a two-argument “pick the better one” function, or Python’s built-inmaxwith akey=.)search(songs, genre, min_year): combine ideas 2 and 4 — return the display strings (not the raw dictionaries) for every song ingenrereleased inmin_yearor 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.
Feedback
Section titled “Feedback”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.
Grading and requirements
Section titled “Grading and requirements”- 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_playedandtotal_durationhandle an empty list sensibly (decide what that should mean, and say so in a comment). Hint: Bothmax()andreduce()accept an optional argument for exactly this situation.- Test code demonstrates each function against the sample data, with results you’ve verified by hand.