Help to Create a Macro - List / Delimiters / Item Enumeration

Hi folks!

I need help to create a macro and will try to describe the problem I need to solve here:

Imagine you have a list:

Fruits

Apples
Bananas
Oranges
Grapes
Lemons

I would like to have the ability to select one or multiple itens on a list, using checkboxes, displaying the result like this:

I bought apples.

I bought apples and oranges.

I bought apples, oranges and grapes.

Can you help me?

Here is a quick sketch of an approach which:

  1. Keeps the list in a Keyboard Maestro variable,
  2. and uses an Execute JavaScript for Automation action.

Others may be able to show you approaches which don't use a script action.

Menu choices report string.kmmacros (22.8 KB)

JS Source
(() => {
    'use strict';

    // Rob Trew 2020

    // Sketch of a menu choices report string with
    // (non-Oxford) commas and a penultimate 'and'.

    // main :: IO ()
    const main = () => {
        const
            kmVarName = 'itemList',
            items = Application('Keyboard Maestro Engine')
            .getvariable(kmVarName)
            .split('\n')

        return either(
            // Explanatory message (e.g. user cancelled)
            alert('Choices')
        )(
            choiceReport
        )(
            1 < items.length ? (
                showMenuLR(true)('Choices')(
                    '⌘-click one or more:'
                )(items)(items[0])
            ) : 0 < items[0].length ? (
                Right(items[0])
            ) : Left(`No list found in KMVar :: ${kmVarName}`)
        );
    };

    // ----------------- REPORTED CHOICE -----------------

    // choiceReport :: [String] -> String
    const choiceReport = choices => {
        const
            xs = choices.map(toLower),
            intItems = xs.length,
            listString = 0 < intItems ? (
                1 < intItems ? (
                    `${init(xs).join(', ')} and ${last(xs)}`
                ) : xs[0]
            ) : 'nothing';

        return `I bought ${listString}.`
    }

    // ----------------------- JXA -----------------------

    // alert :: String => String -> IO String
    const alert = title =>
        s => {
            const sa = Object.assign(
                Application('System Events'), {
                    includeStandardAdditions: true
                });
            return (
                sa.activate(),
                sa.displayDialog(s, {
                    withTitle: title,
                    buttons: ['OK'],
                    defaultButton: 'OK'
                }),
                s
            );
        };

    // showMenuLR :: Bool -> String -> String -> 
    // [String] -> String -> Either String [String]
    const showMenuLR = blnMult =>
        // An optionally multi-choice menu, with 
        // a given title and prompt string.
        // Listing the strings in xs, with 
        // the the string `selected` pre-selected
        // if found in xs.
        title => prompt => xs =>
        selected => 0 < xs.length ? (() => {
            const sa = Object.assign(
                Application('System Events'), {
                    includeStandardAdditions: true
                });
            sa.activate();
            const v = sa.chooseFromList(xs, {
                withTitle: title,
                withPrompt: prompt,
                defaultItems: xs.includes(selected) ? (
                    [selected]
                ) : [xs[0]],
                okButtonName: 'OK',
                cancelButtonName: 'Cancel',
                multipleSelectionsAllowed: blnMult,
                emptySelectionAllowed: false
            });
            return Array.isArray(v) ? (
                Right(v)
            ) : Left('User cancelled ' + title + ' menu.');
        })() : Left(title + ': No items to choose from.');


    // --------------------- GENERIC ---------------------
    // https://github.com/RobTrew/prelude-jxa

    // Left :: a -> Either a b
    const Left = x => ({
        type: 'Either',
        Left: x
    });


    // Right :: b -> Either a b
    const Right = x => ({
        type: 'Either',
        Right: x
    });


    // bindLR (>>=) :: Either a -> 
    // (a -> Either b) -> Either b
    const bindLR = m =>
        mf => undefined !== m.Left ? (
            m
        ) : mf(m.Right);


    // either :: (a -> c) -> (b -> c) -> Either a b -> c
    const either = fl =>
        // Application of the function fl to the
        // contents of any Left value in e, or
        // the application of fr to its Right value.
        fr => e => 'Either' === e.type ? (
            undefined !== e.Left ? (
                fl(e.Left)
            ) : fr(e.Right)
        ) : undefined;

    // init :: [a] -> [a]
    const init = xs => (
        // All elements of a list except the last.
        ys => 0 < ys.length ? (
            ys.slice(0, -1)
        ) : undefined
    )(xs);

    // last :: [a] -> a
    const last = xs => (
        // The last item of a list.
        ys => 0 < ys.length ? (
            ys.slice(-1)[0]
        ) : undefined
    )(xs);

    // toLower :: String -> String
    const toLower = s =>
        // Lower-case version of string.
        s.toLocaleLowerCase();

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

Hi @Pedro.Carmo, welcome to Keyboard Maestro (KM) and its Forum.
KM is one of the best Mac automation tools available, its Forum is one of the best and friendliest forums on the Internet. Whenever you reach a tough stumbling block trying to use KM, please feel free to post your question/problem here for help.

You will also find this helpful:
Tip: How Do I Get The Best Answer in the Shortest Time?

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It is much better to provide real-world data for both your input and output results.

Instead of checkboxes, I'd suggest using the Prompt With List action where you can select multiple items.

Here's an example Macro:

Example Output

image

Example Results

image

Below is just an example written in response to your request. You will need to use as an example and/or change to meet your workflow automation needs.

Please let us know if it meets your needs, or if you have follow-up questions.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MACRO:   Select Multiple Items from a List [Example]

-~~~ VER: 1.0    2020-11-19 ~~~
Requires: KM 8.2.4+   macOS 10.11 (El Capitan)+
(Macro was written & tested using KM 9.0+ on macOS 10.14.5 (Mojave))

DOWNLOAD Macro File:

Select Multiple Items from a List [Example].kmmacros
Note: This Macro was uploaded in a DISABLED state. You must enable before it can be triggered.


1 Like

Perhaps worth adding a case-shift for the output there ?

Thank you.

Interest solution!

Thank you @JMichaelTX.

Interest solution.

I wonder if I could use checkboxes instead of Prompt with List...

Another Question for you @JMichaelTX...

Is it possible to select multiple items using just the keyboard, without mouse and the trackpad?

Thanks for you help.

Yes. But they would have to be hard-coded in the Prompt for User Input action, whereas the Prompt with List can use other sources for the list.

No. Perhaps @peternlewis could add this to his feature request list.

Noted.

2 Likes

Shift selection in the Prompt With List (multiple selection) is done for the next version.

2 Likes