Design a music player with favorites cap
Company: Rippling
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates a candidate's ability to design and implement in-memory data structures, stateful APIs, concurrency handling, deterministic policy enforcement for a fixed-capacity favorites feature, and reasoning about edge cases like removing the currently playing track or handling invalid IDs.
Constraints
- 0 <= len(operations) <= 20000
- Song IDs are integers; metadata is a string of length at most 100
- Favorites capacity is exactly 3; queue entries may contain duplicate song IDs
- An efficient solution should use hash-based lookup plus queue/history structures and process most commands in amortized O(1)
Examples
Input: [('addSong', 1, 'A'), ('addSong', 2, 'B'), ('addSong', 3, 'C'), ('play', 1), ('queueSong', 2), ('queueSong', 3), ('next',), ('getNowPlaying',), ('prev',), ('pause',), ('getNowPlaying',), ('addFavorite', 1), ('addFavorite', 2), ('addFavorite', 3), ('listFavorites',)]
Expected Output: ['OK', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK', (2, 'B', 'playing'), 'OK', 'OK', (1, 'A', 'paused'), 'OK', 'OK', 'OK', [1, 2, 3]]
Explanation: Basic flow: play song 1, queue songs 2 and 3, move next to 2, go back to 1, pause it, then fill the favorites list to its capacity.
Input: [('addSong', 10, 'X'), ('addSong', 10, 'X2'), ('play', 99), ('next',), ('prev',), ('pause',), ('addFavorite', 10), ('addSong', 11, 'Y'), ('addSong', 12, 'Z'), ('addSong', 13, 'W'), ('addFavorite', 11), ('addFavorite', 12), ('addFavorite', 13), ('listFavorites',)]
Expected Output: ['OK', 'ERROR: DuplicateSong', 'ERROR: SongNotFound', 'ERROR: NoNextSong', 'ERROR: NoPreviousSong', 'ERROR: NothingPlaying', 'OK', 'OK', 'OK', 'OK', 'OK', 'OK', 'ERROR: FavoritesFull', [10, 11, 12]]
Explanation: Covers duplicate song insertion, invalid play, navigation on empty history/queue, pausing when nothing is selected, and deterministic rejection of a fourth favorite.
Hints
- Use a hash map for the song library, a deque for queued songs, and two stacks/lists for backward and forward navigation.
- When a song is removed, you do not have to eagerly scan and delete it from every history structure. Lazy deletion works well: skip invalid song IDs when `next()` or `prev()` reaches them.