Here is a way to shuffle a list that needs nothing more than the random[]
operator.
\define shuffle() [enlist<accumulator>remove<currentTiddler>append<currentTiddler>join[ ]]
\define map-position-to-item() [enlist<accumulator>nth<currentTiddler>reduce<shuffle>,<accumulator>]
<$let itemList="a b c d e f g h i j"
numberOfItems = {{{ [enlist<itemList>count[]] }}}
randomizer={{{ [range<numberOfItems>,[1]] :map[random[1],<currentTiddler>] +[join[ ]] }}}>
<$list filter="[enlist:raw<randomizer>reduce<map-position-to-item>,<itemList>enlist-input[]]" template="$:/core/ui/ListItemTemplate" />
</$let>
This was one of the harder pieces of code to figure out, because I repeatedly ran into things that were not possible to do with pure WikiText.
It uses two nested reduce[]
filters, the first of which basically only transforms the current index in the randomizer
list to a tiddler title according to the current order in the accumulator
(whose initial value is the itemList
). The inner shuffle
then moves that title to the end of the list. Basically, an item is chosen at random from the list, moved to the end, and the next item is chosen from the remaining items on the original list. After each item of the original list has been moved, only the shuffled list remains.
If the move[]
filter allowed for a variable parameter N
instead of immutably defining it in its suffix, then this complex nested structure might have been avoided.
It got also a bit more complicated since I wanted to use a pregenerated randomizer
together with an item list that could be generated or changed by refresh. In the above snippet, the list is reshuffled with every keypress or click. But a pregenerated randomizer can be made persistent until changed by an action, as shown in the plugin docs. Hence, a number of randomizer
s could be generated e.g. at startup and used later. The exact number of items per randomizer
need not be known in advance, it just has to be at least as large as the largest list to be shuffled later. One can then use [enlist:raw<randomizer>last<numberOfItems>]
to get a matching sublist.
Alternatively, one could generate a randomizer
of the correct length for a given list with an ActionWidget on demand.
I verified that the list shuffled this way is truly random. Here is a histogram of the 5th item of the shuffled list for 100,000 repetitions (each starting with an unshuffled list, of course):
Since I used MATLAB, my unshuffled list was not “a b c …”, but “1 2 3 …”.
The other items are distributed the same.
Have a nice shuffle
Yaisog