Design a class MusicPlayer that supports the following operations:

play(songId: int): Play a song. If the song is already in the playlist, move it to the end (most recently played).
getRecentlyPlayed(n: int): Return the n most recently played unique songs. If there are fewer than n songs in the playlist, return all of them.
getTopSongs(n: int): Return the n most played songs, sorted by play count (descending). If multiple songs have the same play count, sort them by most recently played.

This problem would test your ability to design a system that efficiently manages a playlist, keeps track of play counts, and retrieves recently played and top songs.

Okay let’s see:

 
class MusicPlayer {
	songs: Map<number, number>
	playList: number[]
 
	constructor(){
		songs: Map<number, number> = new Map()
		playlist: number[] = []
	}
	
	playSong(songId: number){
		if (this.songs.has(songId)){
			let s = this.playlist.indexOf(songId)
			this.playlist = [
				...this.playlist.slice(s),
				...this.playlist.slice(s+1, this.playList.length)
			]
			this.songs.set(songId, this.songs.get(songId)+1)
		} else {
			this.songs.set(songId, 1)
		}
		this.playlist.push(songId)
	}
	
	getRecentlyPlayed(n: number): number[] {
		// let's say n is 1
		let pLength = this.playlist.length
		if (n >= pLength) return this.playlist
		return this.playlist.slice(pLength-n, pLength)
		// return n most recently played unique songs
		// if fewer than n songs, return all
	}
	
	getTopSongs(n: number): number[] {
		// Return the n most played songs, sorted by play count (descending).
		// If multiple songs have the same play count, sort them by most recently played.  
	
		for (let i = 0; i < this.playlist.length; i++){
			let plays = this.songs.get(songId)
			this.songs.set({plays: plays, index: i})
		}
		const songCount = this.songs.size
		return [...this.songs.entries()]
			.sort((a, b)=>b[1].plays-a[1].plays)
			.sort((a, b)=>b[1].index-a[1].index)
			.slice(songCount-n, songCount)
		
	}
}

I actually right that code right here in Obsidian. Then I brought it into VScode and cleaned it up to be, eventually, this:

interface hit {
  plays: number
  index: number
}
 
export class MusicPlayer {
	songs: Map<number, number>
	playlist: number[]
 
	constructor(){
		this.songs = new Map()
		this.playlist = []
	}
	
	playSong(songId: number){
    this.playlist = this.playlist.filter(e=>e!==songId)
    this.playlist.push(songId)
    this.songs.set(songId, (this.songs.get(songId) || 0) + 1)
	}
	
	getRecentlyPlayed(n: number): number[] {
		// let's say n is 1
		let pLength = this.playlist.length
		if (n >= pLength) return this.playlist
		return this.playlist.slice(pLength-n, pLength)
		// return n most recently played unique songs
		// if fewer than n songs, return all
	}
	
  getTopSongs(n: number): number[] {
    const hitList: Map<number, hit> = new Map();
 
    for (let i = 0; i < this.playlist.length; i++) {
      let songId = this.playlist[i];
      let plays = this.songs.get(songId) || 0;
      hitList.set(songId, { plays: plays, index: i });
    }
    
    const result = [...hitList.entries()]
      .sort((a, b) => b[1].plays - a[1].plays || b[1].index - a[1].index)
      .map(([id, _]) => id) // Keep only the song IDs
    return result.slice(0, n);
  }
}
 
const player = new MusicPlayer()
player.playSong(1)
player.playSong(2)
player.playSong(1)
 
// .map(({plays, _})=>plays) <- for objects I could do this

But I think I spent two hours on this. I know. It was rough. I was very close, but I got super stuck. I believe that my playSong method was messed up and not adding things correctly.

So algorithmically, things were good almost from the beginning. A couple things to tighten up, but otherwise great.

Codewise, writing everything in one without verifying that the first functions worked was a huge no-no and made it very hard to debug things.