Can be done with grep in the shell, of course, but I do it quite a lot, and find it a little quicker to experiment with the help of the simple options of a Keyboard Maestro action. ( Saves me checking man grep for simple things at the command line, and lets me keep to the familiar Javascript dialect of Regular Expressions :- )
Sorry post in an old thread, but would it be possible to have the PATTERN be dynamic? it is currently hardcoded. Ideally I'd like to be the result of a Prompt Action
Hello, thank you for your reply. It does not work with the %Variable%pattern%, but neither does it work with regular text. Given the macro is 7 years old it might not be compatible with the latest KM
EDIT: I haven't added anything new here, but I thought I should post here anyway to help other users who are looking for a macro that removes (keeps/maintains) lines that meet certain conditions. Kind of SEO :).
(Apart from replacing the incumbent at ~/Library/Application Support/Keyboard Maestro/Keyboard Maestro Actions, I think you may also need to stop and restart the Keyboard Maestro engine, and/or reimport the custom action to your macro)
FWIW this version (small edit), just aims to replace the supplied pattern string with the result of applying Application("Keyboard Maestro Engine").processTokens to it (with the current instance identifier)
PS @kcwhat if you want to experiment with writing your match predicates as JS x => x.match("??") lambdas, then you may get more flexibility out of a subroutine like this (test macro followed by subroutine below)
Here, for example, partitioning the input list into a pair of JSON output lists (matching and non-matching lines) where the test is for lines which include "o" but not "e".
You can use JS string prototype functions like .matches, which accepts JS regular expressions, or things like .startsWith, .endsWith, or .includes
const main = () => {
const p = Function(
`return ( ${kmvar.local_Predicate} )(arguments[0])`
);
return JSON.stringify(
partition(p)(
kmvar.local_Source.split("\n")
)
);
};
// first :: (a -> b) -> ((a, c) -> (b, c))
const first = f =>
([x, y]) => [f(x), y];
// second :: (a -> b) -> ((c, a) -> (c, b))
const second = f =>
([x, y]) => [x, f(y)];
// partition :: (a -> Bool) -> [a] -> ([a], [a])
const partition = p =>
// A tuple of two lists - those elements in
// xs which match p, and those which do not.
xs => [...xs].reduce(
(a, x) => (
p(x)
? first
: second
)(ys => [...ys, x])(a),
[[], []]
);
return main();
While you are a hero of mine, you may be giving me WAAAAAAY too much credit. I still parse and catch up from posts, of yours and the other km gurus, from years past. I don’t know JSON or any other programming languages but Keyboard Maestro and this forum’s resources give me “Jedi-like”powers. I cut and paste the code with the best of them. I will bookmark this and try to understand. From time to time, the lights do flash though!
Thank you for the lesson and lessons! Keep posting your brilliant plugins. I still use this one and others from a decade ago !
In case you do experiment with JS, and you are partitioning very long lists of lines, then a faster implementation of partition might be something like:
// partition :: (a -> Bool) -> [a] -> ([a], [a])
const partition = p =>
// A tuple of two lists - those elements in
// xs which match p, and those which do not.
xs => {
const
matches = [],
nons = [];
xs.forEach(x => {
if (p(x)) {
matches.push(x)
} else {
nons.push(x)
}
});
return [matches, nons];
};