Update: here the requirements that I followed are spelled out:
- Regardless of how many songs are in the table or at the current location in the playlist, the information that the client must store (for example, in a cookie) must be of a constant size.
- Using "next" 100 times, followed by "prev" 100 times, should result in some sequence of 100 songs (possibly including duplicates), after which this exact sequence will be canceled regardless of any insert that could be made between any "closest" or "prev". ("Previous song always means previous song.")
- A song that would be on the playlist โwhen it was generated / initializedโ and has since been deleted will be skipped.
- It will not be difficult to change my answer below to return an indicator not found.
I think you need one more piece of information: an additional key for the song of the whole position, in addition to your GUID (or you can replace it with this). Then, in combination with PRNG , you can do the simplest and fastest search. Pseudocode:
def next_song(initial_seed, current_prng_index, high_song_index): """Returns the next song and the parameters to be later passed to next/prev_song.""" while True: current_prng_index += 1 current_seed = PRNG_advance(initial_seed, current_prng_index) song_index = song_index_from_seed(current_seed, high_song_index) song_id = (SELECT SongID FROM Songs WHERE SongIndex=song_index) if song_id:
This will gradually slow down in each next song and will not allow you to wrap it, coming back (without any creativity). If you find a suitable reversible PRNG (although this is rare among good PRNGs, AFAIK):
def next_song(current_seed, high_song_index): while True: current_seed = PRNG_next(current_seed) song_index = song_index_from_seed(current_seed, high_song_index) song_id = (SELECT SongID FROM Songs WHERE SongIndex=song_index) if song_id:
This handles the deletion by skipping these songs (or if you never delete it is not even a problem), but otherwise it does not change the order, no matter how many are deleted. The inserts are processed by the song_index_from_seed function, restraining the index, so new songs are never โvisibleโ. It is also an endless loop if all available songs have been deleted, but I think this code handles all other corner cases.
(Refactoring next / previous versions for simple wrappers around a common function is not difficult, but there is no point in increasing clarity.)
I have effectively replaced your dateAdded with my position index, but this is an improvement since you do not need to save deleted rows as mannequins (but still cannot reuse their index), as if the position index for the order for the date Added has been calculated.
Using PRNG, as it seems naive, but works; and you have to be careful that the selected combination of PRNG and song_index_from_seed behaves as you expect: it is possible that some source samples generate "non-random" playlists. (Not because they are not random, but because listeners expect a combination of songs instead of 4, 4 , 9, 9 , ...)