Search Using Regular Expression (Search Variable with Variable)

Problem to Solve.kmmacros (6.8 KB) I created a variable containing a text from a webpage and try to extract a group of text with a specific code using Regular Expression. But the problem I want to get specific code only from a variable. I will show my macro so it is easy to understand.

So this is macro

Prompt for User Input “Code”
Save to Variable Name: code

232

Execute AppleScript
Save to Variable name: Comment

1

Sorry, we're having trouble playing this video.

Belina Daan Gasco is live now in Test.

Just now ·

1 Comment

Like

Comment

Comments

Hide

Comments

Belina Daan Gasco
mine 232

Like

· Reply · Pin · 1m

Write a comment...

Prime Gasco
mine 261

Like

· Reply · Pin · 1m

Write a comment...

Chlo Daan
mine 241

Like

· Reply · Pin · 1m

Write a comment...

1

93

Your comment was submitted

Search Variable “Comment” Using Regular Expression (ignoring case)
Regex

(.+
.+mine\s+\d+).+|(.+
.+\d+\s+mine.+)

Here is the Problem I want to insert Variable in my Regex but how?
I want to like this:

(.+
.+mine\s+%variables%code%).+|(.+
.+%variables%code%\s+mine.+)

I'm doing this because I want to get a specific result

Belina Daan Gasco
mine 232

The result should Based on what my variable:code number contain

Save to Variable name: FBFilter

I don't know how to make this work.
I need help.

@peternlewis
@DanThomas
@JMichaelTX

The others will help you get to the multi-line regex approach working, but FWIW, I find that the productivity of fiddling around with Regex,

(reliability and ease of maintenance divided by time spent) 

proves repeatedly disappointing.

( See: Jamie Zawinski - Wikiquote )

Generally quicker to write and easier to maintain in terms like this:

s.startsWith('mine ') && s.endsWith(numberString)

You have to learn a little JavaScript, or something similar, but it doesn't take nearly as long as the sum of all that Regex-fiddling development and down-time :slight_smile:

Name line from code line.kmmacros (24.0 KB)

Thank you, your solution is quicker than mine, I solve it also but using Regex with For functions. It takes 10 secs to get the result lol. Thank you, I will use this. and I will practice JavaScript.

1 Like

By the way there are problems, if a person type "mine 232 please!" the javascript you made is not working and if a person type please "Please mine 232" or "232 mine please"? do you have another solution for this? @ComplexPoint

Of course :slight_smile:

For a total function over the whole set of possible input strings, we would probably need a fuller sample, to see all of the patterns that can arise,

but a first experiment, would be to replace the startsWith and endsWith expression with a pair of includes. For example:

return s.includes('mine') && (
    s.includes(numberString)
)
JS source
(() => {
    'use strict';
    const main = () => {
        const
            kme = Application('Keyboard Maestro Engine'),
            [inputText, numberString] = [
                'inputText', 'numberString'
            ]
            .map(k => kme.getvariable(k));

        const
            lineList = lines(inputText),
            linePairs = zip(lineList)(
                tail(lineList)
            );

        return maybe(
            // Failure.
            'Not found: ' + numberString
        )(
            // Success.
            ab => {
                // The name is the first line of
                // a two-line pair.
                const name = fst(ab);
                return (
                    // Effect.
                    kme.setvariable('FBFilter', {
                        to: name
                    }),
                    // Value.
                    name
                )
            }
        )(
            // Search.
            find(
                ab => {
                    const s = snd(ab);

                    // Instead of:
                    // return s.startsWith('mine ') && (
                    //     s.endsWith(' ' + numberString)
                    // )

                    return s.includes('mine') && (
                        s.includes(numberString)
                    )
                }
            )(
                // Each line zipped together with
                // the following line.
                zip(lineList)(
                    tail(lineList)
                )
            )
        )
    };

    // ------------- GENERAL LIBRARY FUNCTIONS -------------
    // https://github.com/RobTrew/prelude-jxa

    // Just :: a -> Maybe a
    const Just = x => ({
        type: 'Maybe',
        Nothing: false,
        Just: x
    });


    // Nothing :: Maybe a
    const Nothing = () => ({
        type: 'Maybe',
        Nothing: true,
    });


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


    // find :: (a -> Bool) -> [a] -> Maybe a
    const find = p =>
        // Just the first element in xs which 
        // matches the predicate p, or
        // Nothing if no match is found.
        xs => xs.constructor.constructor.name !== (
            'GeneratorFunction'
        ) ? (() => {
            const
                ys = list(xs),
                i = ys.findIndex(p);
            return -1 !== i ? (
                Just(ys[i])
            ) : Nothing();
        })() : findGen(p)(xs);


    // fst :: (a, b) -> a
    const fst = tpl =>
        // First member of a pair.
        tpl[0];


    // length :: [a] -> Int
    const length = xs =>
        // Returns Infinity over objects without finite
        // length. This enables zip and zipWith to choose
        // the shorter argument when one is non-finite,
        // like cycle, repeat etc
        'GeneratorFunction' !== xs.constructor
        .constructor.name ? (
            xs.length
        ) : Infinity;


    // lines :: String -> [String]
    const lines = s =>
        // A list of strings derived from a single
        // newline-delimited string.
        0 < s.length ? (
            s.split(/[\r\n]/)
        ) : [];


    // list :: StringOrArrayLike b => b -> [a]
    const list = xs =>
        // xs itself, if it is an Array,
        // or an Array derived from xs.
        Array.isArray(xs) ? (
            xs
        ) : Array.from(xs || []);


    // maybe :: b -> (a -> b) -> Maybe a -> b
    const maybe = v =>
        // Default value (v) if m is Nothing, or f(m.Just)
        f => m => m.Nothing ? v : f(m.Just);


    // snd :: (a, b) -> b
    const snd = tpl =>
        // Second member of a pair.
        tpl[1];


    // tail :: [a] -> [a]
    const tail = xs =>
        // A new list consisting of all
        // items of xs except the first.
        'GeneratorFunction' !== xs.constructor
        .constructor.name ? (
            (ys => 0 < ys.length ? ys.slice(1) : [])(
                list(xs)
            )
        ) : (take(1)(xs), xs);


    // take :: Int -> [a] -> [a]
    // take :: Int -> String -> String
    const take = n =>
        // The first n elements of a list,
        // string of characters, or stream.
        xs => 'GeneratorFunction' !== xs
        .constructor.constructor.name ? (
            xs.slice(0, n)
        ) : [].concat.apply([], Array.from({
            length: n
        }, () => {
            const x = xs.next();
            return x.done ? [] : [x.value];
        }));


    // zip :: [a] -> [b] -> [(a, b)]
    const zip = xs =>
        // Use of `take` and `length` here allows for 
        // zipping with non-finite lists - i.e. generators 
        // like cycle, repeat, iterate.
        ys => (([xs_, ys_]) => {
            const
                n = Math.min(...[xs_, ys_].map(length)),
                vs = take(n)(ys_);
            return take(n)(xs_).map(
                (x, i) => Tuple(x)(vs[i])
            );
        })([xs, ys].map(list));

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

Now it works in any combination of string, You are a Genius, I cannot fully understand the javascript compare to AppleScript, I definitely need to learn this, right now, comparing your two codes, trying to understand. Thank you again. @ComplexPoint

1 Like

If the pattern is always:

"a line that has a name"
"a line that includes the code"

This should work (it's quite simple; I will not upload the macro):

2 Likes

Wow, very simple I dont know I can use variables inside regex. Thank you @martin

You're welcome.

Just add one more note: this RegEx will return only the first match.
If two persons have the same number code (e.g., 232), and you want to find both of them, it will not work. In this case, you will have to use the "For Each" action and the "Append text to Variable" action.
An example may be found here: