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