Get randrom value which not duplicate with the value in previous random list

Hi, every one. As everyone know that we can get random value with construction as example below

{{["media_player.video_url_1","media_player.video_url_2", "media_player.video_url_3"] | random}}

I have script with repeat to play random youtube audio with a list of url links.
The problem is that the random get too much same value urls when play next song so that I have to hear that song repeatly.
So Anyone have any idea to solve that problem. Thanks

For example:

I have list with 10 songs: audio_1…audio_10
I’ll random it in the list and get the audio_3. At the next song, the audio_3 will be remove from the list and random the others one. And continue repeat to the last ones.

I encountered a similar challenge with changing the color of pool lights. There’s a set of 8 colors from which one would be randomly selected. However, it didn’t “feel” random because sometimes the same color would be selected twice in a row.

The solution is to initially randomize the order of the set of colors, namely to “shuffle the deck”, and then display the colors in sequence.

There’s no shuffle filter in Jinja2 so the randomization (shuffling) has to be be done manually. With some help from pnbruckner, I managed to implement the Fisher-Yates shuffling algorithm as a template.

I helped someone else implement it for randomizing the order of a room-mopping scripts:

Let me know if it interests you and I can help you use it for randomizing the order of the media_players songs.

For example, copy-paste this into the Template Editor and experiment with it. Replace the list’s contents with whatever items you require (like songs).

{% set players = [ "media_player.video_url_1", "media_player.video_url_2",
                    "media_player.video_url_3"] %}

{% set ns = namespace(x = players) %}
{% for i in range(ns.x | length - 1, 0, -1) %}
    {% set j = range(0, i + 1) | random %}
    {% if j != i %}
      {% set ns.x = ns.x[:j]+[ns.x[i]]+ns.x[j+1:i]+[ns.x[j]]+ns.x[i+1:] %}
    {% endif %}
{% endfor %}
{{ ns.x }}

Screenshot of the shuffled list of players:


When you run the script, if the random result matches the last played result, run the script again until it doesn’t match?

That would work to eliminate the same song from playing twice in a row. What it wouldn’t do is eliminate the possibility of hearing it repeated within the set.

In other words, it wouldn’t prevent this:

song 1
song 3
song 1
song 2
song 4
song 3
song 4

Just thinking out loud here.

What if we have an array with all possible songs.
When play is started this array is copied to an input_text, then when a song needs to be picked the input text is read and one is picked from the array/text and removed.
When the input_text is empty it copies the complete list again?

FWIW, the concept of shuffling a set of songs and then playing the shuffled set in sequence is basically the function available in most media-playing apps. Emulating it with a template is challenging because there’s no shuffle filter. However, that’s what that for-loop does in the example posted above. Given a list, it produces a shuffled list. All that’s left is to simple iterate through the shuffled list; no song is repeated until the end of the list.

There’s no need to remove songs from the list. Shuffle the list and then play each song in sequence.

1 Like

I agree, just shuffle then play in the new shuffled order

And your shuffle function works like a charm, also when I increased the list length. But I do’nt really understand the logic of it :slight_smile:

Very nice solution, :+1:

If you want to know more, check the posted link for the Fisher-Yates algorithm. It explains its operation and shows examples in several different programming languages. I used the python example as the basis for creating a Jinja2 template. As mentioned, I needed help with the list construction because list handling is rather limited in Home Assistant’s implementation of Jinja2. To get a sense of that, compare the python version to the Jinja2 version.

If the shuffle function has satisfied your requirements, please consider marking my post above with the Solution tag. It will automatically place a check-mark next to the topic’s title which signals to other users that this topic is resolved. This helps other users find answers to similar questions. For more information, refer to guideline 21 in the FAQ.

I came here to mention the Fisher-Yates algorithm too. I implemented it years ago on a project as a way to make (computationally easy and cheap) recommendations to users of an app. Anyway, it works and is a good suggestion. I also wanted to say that humans have a hard time believing true randomness. I can unfortunately not find a good reference for this now, but I remember from my research back then that Apple did implement Fisher-Yates on the iPod to shuffle music, but users didn’t like it. Here is a close article written about the effect: I’m mentioning this, because you might implement this and find it not satisfactory.

Do you have any other sources because there’s not really a lot of substance in that one.

The first part seems likes it was due to end-user’s misunderstanding of the player’s shuffle function (it doesn’t keep re-shuffling the order, only when you request it). The second part doesn’t explain what Spotify did to satisfy customer expectations of randomness.


Nevermind, I followed the rabbit hole to here:

some people don’t want the same artist playing two or three times within a short time period

That’s solved by involving more keys than merely one (song title). I fell that’s not a failing of the shuffling algorithm but how it was used.

Hi @123
Unfortunately, I don’t have the option to mark a post as solution.
Probably it is only for the one who started the thread.

Oops! You’re right; my mistake. The author of this thread hasn’t replied yet.

Anyway, if it helped you as well then that’s a bonus.

Thanks so much for reply,
that’s a great idea. It’s solve out my problem perfectly.
Your topic you share help me alot in making my own script. Thank you so much

1 Like

Hi @123

Now that I understand the logic, I guess you can do a little optimization.

The range from which J is generated should not go to i+1 but i-1 because:

  • You are not interested in changing values that are higher than i (These have been handled)
  • and you are not interested in having i=i

and this way you don’t need the if close.

The result for the loop becomes:

{% for i in range(ns.x | length - 1, 1, -1) %}
    {% set j = range(0, i - 1) | random %}
      {% set ns.x = ns.x[:j]+ [ns.x[i]]+ns.x[j+1:i]+[ns.x[j]]+ns.x[i+1:] %}
{% endfor %}

Please try it and let me know what you think.

Kind regards,

1 Like

shuffle filter is now available in Spook 2.0.0

1 Like