Comparing one variable to a list of others

Hi All,

I'm not sure if I'm spacing out or not, but I'm simply trying to compare the text value of 30 variables to 30 other text variables in order to find a match. So for example, I have 2 sets of 30 total variables:

List one:
1-Variable 1, 1-Variable 2, 1-Variable 3...etc.

List two:
2-Variable 2, 2-Variable 2, 2-Variable 3...etc.

I'd like to return a "true" response if, for example, 1-Variable 29 is an exact match to 2-Variable 12.

I'm open to any and all suggestions!

Here is one scripted approach:

Variable matches in two lists.kmmacros (22.0 KB)

32

matchVals Variable matches in two lists.kmmacros (22.0 KB)

JS source

(() => {
    'use strict';

    const main = () => {

        const
            // Any number of KM variable names ...
            kxs = ['name1', 'name2', 'name3'],

            // A second list of any no. of KM variable names ...
            kys = ['name4', 'name5', 'name6'];

        // MAIN -------------------------------------------------
        const main = () =>
            matchPairs(kxs, kys);

        // VARIABLES WITH MATCHING VALUES  ----------------------

        // matchPairs :: [String] -> [String] ->
        //                  [((String, String), (String, String))]
        const matchPairs = (kmVarNameList1, kmVarNameList2) =>
            concatMap(
                xy => snd(fst(xy)) === snd(snd(xy)) ? (
                    [xy]
                ) : [],
                cartesianProduct(
                    map(keyVal, kmVarNameList1),
                    map(keyVal, kmVarNameList2)
                )
            );

        const
            kme = Application('Keyboard Maestro Engine'),
            keyVal = k => [k, kme.getvariable(k)];

        // TEST
        return JSON.stringify(
            main()
        );
    };

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

    // Tuple (,) :: a -> b -> (a, b)
    const Tuple = (a, b) => ({
        type: 'Tuple',
        '0': a,
        '1': b,
        length: 2
    });

    // e.g. [(*2),(/2), sqrt] <*> [1,2,3]
    // -->  ap([dbl, hlf, root], [1, 2, 3])
    // -->  [2,4,6,0.5,1,1.5,1,1.4142135623730951,1.7320508075688772]

    // Each member of a list of functions applied to each
    // of a list of arguments, deriving a list of new values.

    // apList (<*>) :: [(a -> b)] -> [a] -> [b]
    const apList = (fs, xs) => //
        fs.reduce((a, f) => a.concat(
            xs.reduce((a, x) => a.concat([f(x)]), [])
        ), []);

    // cartesianProduct :: [a] -> [b] -> [(a, b)]
    const cartesianProduct = (xs, ys) =>
        apList(xs.map(x => y => [x, y]), ys);

    // concatMap :: (a -> [b]) -> [a] -> [b]
    const concatMap = (f, xs) =>
        xs.reduce((a, x) => a.concat(f(x)), []);

    // fst :: (a, b) -> a
    const fst = tpl => tpl[0];

    // listFromTuple :: (a, a ...) -> [a]
    const listFromTuple = tpl =>
        Array.from(tpl);

    // map :: (a -> b) -> [a] -> [b]
    const map = (f, xs) => xs.map(f);

    // snd :: (a, b) -> b
    const snd = tpl => tpl[1];

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

1 Like

It is not clear to me exactly what you want.
Please read:
Tip: How Do I Get The Best Answer in the Shortest Time?

The key info we need here is a real-world example of your data before and after the macro results. Please put all data in a Code Block.

1 Like

This worked perfectly! Thank you!! I tested this a few times and each time I got the same result as your first screen shot. I'm curious if there is a simple code or another way in KM to turn:

[[["name2","Berlin"],["name4","Berlin"]]]

-into just-

Berlin

If not, I can make this work, and thank you again for the help!
Scott

Just 'berlin'

Something like this, perhaps:

(() => {
    'use strict';

    const main = () => {

        const
            // Any number of KM variable names ...
            kxs = ['name1', 'name2', 'name3'],

            // A second list of any no. of KM variable names ...
            kys = ['name4', 'name5', 'name6'];

        // MAIN -------------------------------------------------
        const main = () =>
            matchPairs(kxs, kys);

        // VARIABLES WITH MATCHING VALUES  ----------------------

        // matchPairs :: [String] -> [String] -> String
        const matchPairs = (kmVarNameList1, kmVarNameList2) =>
            concatMap(
                ([x, y]) => snd(x) === snd(y) ? (
                    // [[x, y]]
                    [snd(y)]
                ) : [],
                cartesianProduct(
                    map(keyVal, kmVarNameList1),
                    map(keyVal, kmVarNameList2)
                )
            ).join('\n');

        const
            kme = Application('Keyboard Maestro Engine'),
            keyVal = k => [k, kme.getvariable(k)];

        // TEST
        return main();
    };

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

    // e.g. [(*2),(/2), sqrt] <*> [1,2,3]
    // -->  ap([dbl, hlf, root], [1, 2, 3])
    // -->  [2,4,6,0.5,1,1.5,1,1.4142135623730951,1.7320508075688772]

    // Each member of a list of functions applied to each
    // of a list of arguments, deriving a list of new values.

    // apList (<*>) :: [(a -> b)] -> [a] -> [b]
    const apList = (fs, xs) => //
        fs.reduce((a, f) => a.concat(
            xs.reduce((a, x) => a.concat([f(x)]), [])
        ), []);

    // cartesianProduct :: [a] -> [b] -> [(a, b)]
    const cartesianProduct = (xs, ys) =>
        apList(xs.map(x => y => [x, y]), ys);

    // concatMap :: (a -> [b]) -> [a] -> [b]
    const concatMap = (f, xs) =>
        xs.reduce((a, x) => a.concat(f(x)), []);

    // map :: (a -> b) -> [a] -> [b]
    const map = (f, xs) => xs.map(f);

    // snd :: (a, b) -> b
    const snd = tpl => tpl[1];

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

1 Like

If there were not only two 'Berlin's, but also two 'Adelaide's this version would yield

Berlin
Adelaide

but it could also be adjusted to keep them on a single line, separated by space or some other delimiter

1 Like

Perfect!!! Exactly what I needed - very much appreciated!!

1 Like

As a footnote, an Applescript translation of the same pattern (tho JS run-time is faster):

-- matchPairs :: [String] -> [String] -> String
on matchPairs(kmVarNames1, kmVarNames2)
    script go
        on |λ|(xy)
            set {x, y} to xy
            set v to item 2 of y
            if v = item 2 of x then
                {v}
            else
                {}
            end if
        end |λ|
    end script
    
    tell application "Keyboard Maestro Engine"
        script keyVal
            property kme : it
            on |λ|(k)
                tell kme to {k, getvariable k}
            end |λ|
        end script
    end tell
    
    unlines(concatMap(go, ¬
        cartesianProduct(map(keyVal, kmVarNames1), ¬
            map(keyVal, kmVarNames2))))
end matchPairs


-- TEST ---------------------------------------------------
on run
    set kxs to {"name1", "name2", "name3"}
    set kys to {"name4", "name5", "name6"}
    
    matchPairs(kxs, kys)
end run


-- GENERIC ------------------------------------------------

-- cartesianProduct :: [a] -> [b] -> [(a, b)]
on cartesianProduct(xs, ys)
    script
        on |λ|(x)
            script
                on |λ|(y)
                    {{x, y}}
                end |λ|
            end script
            concatMap(result, ys)
        end |λ|
    end script
    concatMap(result, xs)
end cartesianProduct

-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
    set lng to length of xs
    set acc to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set acc to acc & |λ|(item i of xs, i, xs)
        end repeat
    end tell
    return acc
end concatMap

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

-- Lift 2nd class handler function into 1st class script wrapper 
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- unlines :: [String] -> String
on unlines(xs)
    set {dlm, my text item delimiters} to ¬
        {my text item delimiters, linefeed}
    set str to xs as text
    set my text item delimiters to dlm
    str
end unlines

This is excellent - works great! Thank you!