perf(player): add idToIndex Map for O(1) track lookups

Replace all linear find/findIndex/some scans with a derived Map
keyed by track.id. This makes currentTrack, currentIndex, hasTrack,
add, playNext, remove, and addAll all O(1) for ID lookups.
This commit is contained in:
2026-02-11 22:56:49 -08:00
parent da3ab81ae6
commit 31414c5874

View File

@@ -26,16 +26,26 @@ class PlayerStore {
isMuted = $state(false); isMuted = $state(false);
uiOpen = $state(false); // Mobile UI state uiOpen = $state(false); // Mobile UI state
// Derived // O(1) index: track.id → index in queue
currentTrack = $derived( private idToIndex = $derived.by(() => {
this.currentId const map = new Map<number, number>();
? (this.queue.find((t) => t.id === this.currentId) ?? null) for (let i = 0; i < this.queue.length; i++) {
: null, map.set(this.queue[i].id, i);
); }
return map;
});
currentIndex = $derived( // Derived
this.currentId ? this.queue.findIndex((t) => t.id === this.currentId) : -1, currentTrack = $derived.by(() => {
); if (this.currentId == null) return null;
const idx = this.idToIndex.get(this.currentId);
return idx !== undefined ? this.queue[idx] : null;
});
currentIndex = $derived.by(() => {
if (this.currentId == null) return -1;
return this.idToIndex.get(this.currentId) ?? -1;
});
displayQueue = $derived( displayQueue = $derived(
this.isShuffled this.isShuffled
@@ -46,7 +56,7 @@ class PlayerStore {
); );
hasTrack(id: number) { hasTrack(id: number) {
return this.queue.some((t) => t.id === id); return this.idToIndex.has(id);
} }
constructor() { constructor() {
@@ -105,9 +115,9 @@ class PlayerStore {
// Actions // Actions
add(track: Track, playNow = false) { add(track: Track, playNow = false) {
const existingIdx = this.queue.findIndex((t) => t.id === track.id); const exists = this.hasTrack(track.id);
if (existingIdx !== -1) { if (exists) {
if (playNow) { if (playNow) {
this.playNext(track); this.playNext(track);
this.playId(track.id); this.playId(track.id);
@@ -133,7 +143,7 @@ class PlayerStore {
} }
playNext(track: Track) { playNext(track: Track) {
const existingIdx = this.queue.findIndex((t) => t.id === track.id); const existingIdx = this.idToIndex.get(track.id) ?? -1;
const targetTrack = track; const targetTrack = track;
if (existingIdx !== -1) { if (existingIdx !== -1) {
@@ -173,7 +183,7 @@ class PlayerStore {
const newTracks: Track[] = []; const newTracks: Track[] = [];
for (const track of tracks) { for (const track of tracks) {
// Check existence inline to avoid O(n) per-track via add() // Check existence inline to avoid O(n) per-track via add()
if (!this.queue.some((t) => t.id === track.id)) { if (!this.hasTrack(track.id)) {
newTracks.push(track); newTracks.push(track);
} }
} }
@@ -200,8 +210,8 @@ class PlayerStore {
} }
remove(id: number) { remove(id: number) {
const idx = this.queue.findIndex((t) => t.id === id); const idx = this.idToIndex.get(id);
if (idx === -1) return; if (idx === undefined) return;
const wasCurrent = this.currentId === id; const wasCurrent = this.currentId === id;