fix: Avoid slices.Contains within tight loops

Having a `slices.Contains` check within a `for` loop has a complexity of
`O(n*m)`, with a worst case scenario of `O(n^2)` for when both
collections have the same size.

This is because `slices.Contains` internally runs just a regular loop,
checking for equality element by element.

Instead, this diff changes every occurence of this pattern to converting
the collection we compare against to a set, and then running a faster
lookup against it.
This commit is contained in:
Michael Manganiello
2024-03-17 13:21:51 -03:00
parent 3d61659d07
commit 80d7cf0b65
3 changed files with 10 additions and 8 deletions
+4 -2
View File
@@ -98,8 +98,9 @@ func ReorderTracks(tracks []*mediaprovider.Track, idxToMove []int, op TrackReord
case MoveToTop: case MoveToTop:
topIdx := 0 topIdx := 0
botIdx := len(idxToMove) botIdx := len(idxToMove)
idxToMoveSet := ToSet(idxToMove)
for i, t := range tracks { for i, t := range tracks {
if slices.Contains(idxToMove, i) { if _, ok := idxToMoveSet[i]; ok {
newTracks[topIdx] = t newTracks[topIdx] = t
topIdx++ topIdx++
} else { } else {
@@ -110,8 +111,9 @@ func ReorderTracks(tracks []*mediaprovider.Track, idxToMove []int, op TrackReord
case MoveToBottom: case MoveToBottom:
topIdx := 0 topIdx := 0
botIdx := len(tracks) - len(idxToMove) botIdx := len(tracks) - len(idxToMove)
idxToMoveSet := ToSet(idxToMove)
for i, t := range tracks { for i, t := range tracks {
if slices.Contains(idxToMove, i) { if _, ok := idxToMoveSet[i]; ok {
newTracks[botIdx] = t newTracks[botIdx] = t
botIdx++ botIdx++
} else { } else {
+2 -1
View File
@@ -257,9 +257,10 @@ func (a *NowPlayingPage) Tapped(*fyne.PointEvent) {
} }
func (a *NowPlayingPage) doSetNewTrackOrder(trackIDs []string, op sharedutil.TrackReorderOp) { func (a *NowPlayingPage) doSetNewTrackOrder(trackIDs []string, op sharedutil.TrackReorderOp) {
trackIDSet := sharedutil.ToSet(trackIDs)
idxs := make([]int, 0, len(trackIDs)) idxs := make([]int, 0, len(trackIDs))
for i, tr := range a.queue { for i, tr := range a.queue {
if slices.Contains(trackIDs, tr.ID) { if _, ok := trackIDSet[tr.ID]; ok {
idxs = append(idxs, i) idxs = append(idxs, i)
} }
} }
+4 -5
View File
@@ -3,7 +3,6 @@ package browsing
import ( import (
"fmt" "fmt"
"log" "log"
"slices"
"github.com/dweymouth/supersonic/backend" "github.com/dweymouth/supersonic/backend"
"github.com/dweymouth/supersonic/backend/mediaprovider" "github.com/dweymouth/supersonic/backend/mediaprovider"
@@ -181,15 +180,15 @@ func (a *PlaylistPage) doSetNewTrackOrder(op sharedutil.TrackReorderOp) {
// Since the tracklist view may be sorted in a different order than the // Since the tracklist view may be sorted in a different order than the
// actual running order, we need to get the IDs of the selected tracks // actual running order, we need to get the IDs of the selected tracks
// from the tracklist and convert them to indices in the *original* run order // from the tracklist and convert them to indices in the *original* run order
ids := a.tracklist.SelectedTrackIDs() idSet := sharedutil.ToSet(a.tracklist.SelectedTrackIDs())
idxs := make([]int, 0, len(ids)) idxs := make([]int, 0, len(idSet))
for i, tr := range a.tracks { for i, tr := range a.tracks {
if slices.Contains(ids, tr.ID) { if _, ok := idSet[tr.ID]; ok {
idxs = append(idxs, i) idxs = append(idxs, i)
} }
} }
newTracks := sharedutil.ReorderTracks(a.tracks, idxs, op) newTracks := sharedutil.ReorderTracks(a.tracks, idxs, op)
ids = sharedutil.TracksToIDs(newTracks) ids := sharedutil.TracksToIDs(newTracks)
if err := a.sm.Server.ReplacePlaylistTracks(a.playlistID, ids); err != nil { if err := a.sm.Server.ReplacePlaylistTracks(a.playlistID, ids); err != nil {
log.Printf("error updating playlist: %s", err.Error()) log.Printf("error updating playlist: %s", err.Error())
} else { } else {