Passing multiple options to an Execute Script action (Dictionaries)

Passing multiple options from the Keyboard Maestro GUI to 'Execute JavaScript for Automation' and 'Execute AppleScript' actions can sometimes involve using several KM variables, inflating Keyboard Maestro's list of variable names.

Keyboard Maestro 8's new Dictionaries now provides a good way of passing a set of key-value option pairs to a script in the form of a single dictionary, rather than as a list of separate variables.

For example, TaskPaper scripts typically involve passing a dictionary of key:value pairs as the withOptions argument to the evaluate() function.

We can now obtain this JavaScript options dictionary directly from the set of keyValue pairs in a single Keyboard Maestro dictionary.

If a section of our macro sets these up:

A generic kmDict function can convert the Keyboard Maestro dictionary to a JavaScript dictionary for us.

ES6 (Sierra onwards) JavaScript example:


 (() => {
    'use strict';

    // GENERIC FUNCTION ------------------------------------------------------

        // kmDict :: String -> Dict
        const kmDict = strName => {
            const kmd = Application('Keyboard Maestro Engine')
                .dictionaries[strName].dictionaryKeys;
            return Object.keys(kmd)
                .reduce((a, k) => {
                    const kv = kmd[k];
                    return (
                        a[kv.name()] = kv.value()
                        .trim(),
                        a
                    );
                }, {});
        };

        // TEST ------------------------------------------------------------------

        return kmDict('tp3Toggle');

        // --> {"parseValueAsDateTime":"1", "subTree":"0", "tag":"incubate"}
    })();

( And if we wanted, we could also derive an AppleScript record from a Keyboard Maestro dictionary, though AS records are always a little bit harder to deal with than JS Dictionaries )

use framework "Foundation"
use scripting additions

-- kmDict :: String -> Record
on kmDict(strName)
    tell application ("Keyboard Maestro Engine")
        set ca to current application
        set dict to dictionary named strName
        script kv
            on |λ|(a, k)
                set nsDct to (ca's NSMutableDictionary's dictionaryWithDictionary:a)
                nsDct's setValue:(value of dictionary key k of dict) forKey:k
                item 1 of ((ca's NSArray's arrayWithObject:nsDct) as list)
            end |λ|
        end script
        my foldl(kv, {name:""}, name of dictionary keys of dict)
    end tell
end kmDict


-- TEST ----------------------------------------------------------------------
on run
    kmDict("tp3Toggle")
    
    --> {notify:"1", parseValueAsDateTime:"1", tag:"incubate", subTree:"0"}
end run


-- GENERIC FUNCTIONS ---------------------------------------------------------

-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with i from 1 to lng
            set v to |λ|(v, item i of xs, i, xs)
        end repeat
        return v
    end tell
end foldl

-- Lift 2nd class handler function into 1st class script wrapper 
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn
2 Likes

Thanks, @ComplexPoint. This looks very useful.
Thanks also for providing both a JXA and an AppleScript solution.

I guess all we need now are functions to go back to KM, to convert JS Object and AS Record into a KM Dictionary. :wink: