Selecting an item from an array at random

I'm trying to select a random item from a comma-delimited list, which I understand KM treats as an array. The length of the array will vary.

set variable "FruitList" to "apples,bananas,lemons,limes" works fine, and
%Variable%FruitList[3]% returns "lemons" as expected.
%Variable%FruitList[0]% returns "4" — the length of the array — as expected.

But I can't use %Variable%FruitList[0]% as the input to a RAND() function. Attempting that generates an error. So I'm not sure how to get a random integer in the range of my array length.

Any help appreciated.

Random Fruit.kmmacros (4.9 KB)

Hi @Lightsleeper,

Try this macro. It illustrates selecting a random fruit.
Random Fruit.kmmacros (4.0 KB)

2 Likes

I will give this a try. Thanks!

Using RAND() in a CALC field or Calculate token (as in @ChrisQ's example) is clearly the natural approach in Keyboard Maestro.

There are of course, also scripted approaches e.g. in JavaScript or Python:

Random Fruit.kmmacros (3.2 KB)


Expand disclosure triangle to view JS source
(() => {
    "use strict";

    // Random pick from lines in local_Fruits
    const main = () => {
        const
            xs = kmValue(kmInstance())("local_Fruits")
            .split("\n")
            .filter(Boolean),
            indexGen = randomRInt(0)(xs.length - 1);

        return xs[indexGen()];
    };


    // ---------------- KEYBOARD MAESTRO -----------------

    // kmInstance :: () -> IO String
    const kmInstance = () =>
        ObjC.unwrap(
            $.NSProcessInfo.processInfo.environment 
            .objectForKey("KMINSTANCE")
        ) || "";


    // kmValue :: KM Instance -> String -> IO String
    const kmValue = instance =>
        k => Application("Keyboard Maestro Engine")
        .getvariable(k, {instance});


    // --------------------- GENERIC ---------------------

    // randomRInt :: Int -> Int -> (() -> IO Int)
    const randomRInt = low =>
        // The return value of randomRInt is itself
        // a function, which, whenever evaluated,
        // yields a a new pseudo-random integer
        // in the range [low..high].
        high => () => low + Math.floor(
            Math.random() * (1 + (high - low))
        );

    // MAIN --
    return main();
})();

1 Like

This did just what I need. Thank you.