Non-repetitive Random Selection of items from a list

Here, I described how you can make a random selection from a list of items … but there are times when you need to make such random selections without repeating any of the selections … this article details how you would go about implementing this with VisualNEO for Windows.

In your App’s StartUp section … code something like this …

SetVar "[myInitialFruits]" "!/Apple,/Banana,/Cherry,/Pear,/Peach,"
SetVar "[myFruits]" "[myInitialFruits]"

Note that each item has a prefix and a suffix character surrounding it ; without the prefix character, it would be difficult to differentiate between items like “NEO” and “VisualNEO” and “VisualNEO Win”

If the items in your list contain a comma or forward slash, you can use different characters to separate the items … e.g. a dollar sign and a semicolon … or even multi-character separators like @#$%.

The StrParse command will create an arrayed variable set from the (comma) separated list of items … so you will end up with variables [myFruitsItem1], [myFruitsItem2] etc. … where [myFruitsItem1] will contain the value /Apple, [myFruitsItem2] will contain the value /Banana and so on.

Then, when you want to get a random item from the list …


If "[myFruits]" "=" ""
  AlertBox "Sorry" "list is empty"
  Return
EndIf
...
StrParse "[myFruits]" "," "[myFruitsItem]" "[myFruitsItemCount]"
If "[myFruitsItemCount]" ">" "1"
  Random "[myFruitsItemCount]-1" "[RandomItemNumber]"
  SetVar "[RandomItemNumber]" "[RandomItemNumber]+1"
  SetVar "[NextRandomItem]" "[myFruitsItem[RandomItemNumber]]"
  ... remove the prefix character
  StrReplace "[NextRandomItem]" "!/" "" "[NextRandomItem]"
  ... remove this item from the list
  StrReplace "[myFruits]" "!/[NextRandomItem]," "" "[myFruits]"
Else
  ... one item left in list ; remove prefix and suffix characters
  StrReplace "[myFruits]" "!/" "" "[myFruits]"
  StrReplace "[myFruits]" "!," "" "[NextRandomItem]"
  SetVar "[myFruits]" ""
EndIf

Depending on the nature of your application, instead of the AlertBox command you may choose another action … like re-load the [myFruits] list from [myInitialFuitsList]

The Random command returns a random number between 0 and the value in the first parameter … since you want a number between 1 and the number of items in the list … you ask for a result between 0 and a number that is one less than the number of items in the list … then you add one to whatever result is returned.

You then use the number returned as the index to get the corresponding array item … so if [RandomItemNumber] was 3, then [NextRandomItem] would contain /Cherry … because [myFruitsItem3] was set to this value by the StrParse command before … and the next StrReplace command will remove the prefix character of “/”.

If your applications need to make such random selections frequently, you can consider making up your own custom commands … so you can implement the same functionality with just 2 commands … something like …

Call "gkExtractListToArray" "!/Apple,/Banana,/Cherry,/Pear" "!/" "," "myFruitsItem"

… and …

If "[myFruits]" "=" ""
  AlertBox "Sorry" "list is empty"
  Return
EndIf
...
Call "gkExtractRandomListItem" "myFruitsItem" "!/" "," "[NextRandomItem]"

Finally, if you have lists with a lot of items … you could keep your list in a .txt file … one item per line … use something like notepad.exe to make up a file that looks like …


/Apple
/Banana
/Cherry
/Peach

… and in your pub’s StartUp section … code something like this …


FileRead "[PubDir]myFruits.txt" "All" "[myInitialFruits]"
StrReplace "[myInitialFruits]" "[#13][#10]" "," "[myFruits]"

… and if you don’t need to change the contents of such lists at run time, you could also mark the file as Embedded … and replace [PubDir]myFruits.txt above with [Embedded]myFruits.txt.