Reusing the same filter for multiple variables

I think you may have been approaching this at the wrong level – that looks like a mixture of encoded and decoded that probably doesn't need to arise.

HTML entities can be encoded with names or numbers – here you can see the same string encoded in two different ways,

&  < > "

&  < > "

and yes, KM's filter actions does provide for encoding as well as decoding:

Encode HTML entities (using names or numbers).kmmacros (3.8 KB)


Screenshot 2024-02-22 at 7.41.48 pm

1 Like

Separately, if you ever do need to use subroutines, here's an example, of:

  1. a subroutine, and
  2. a macro which uses that subroutine.

Subroutine example Macros.kmmacros (4,9 Ko)

1 Like

Not sure that I have a picture of what you are trying to do there.

The Decode HTML Entities action will resolve all entities in a document.

How do the multiple variables arise ?

What's in them ?

I scrape various elements from a large HTML document
I then filter each variable (to decode HTML entities)
Are you suggesting that, rather, I should decode the big HTML document and THEN scrape?

An example of how I scrape



How i then make the text readable

Decoding HTML entities is actually better

Your filter suggestion is better (it saves me even more steps)

But now I have a situation where I want to feed it like 20 or more variables and

ooh thanks for this.
I have downloaded it.
However it gives me a headache trying to understand it. I wish there was a video tutorial.

What is return text ?

Is it like display text?

can do i like this:

Well stepping back a bit, its easier to extract text from a webpage using things like document.querySelector, which completely avoid all these issues with HTML entity decoding.

There are probably plenty of examples here, but you could start by looking at a contribution like:

Extract Data from Web Page and Paste into Mac App [Example] - Macro Library - Keyboard Maestro Discourse

1 Like

In another filter example I am changing some words for emojis like this:

It's just a whimsical thing - not important. But there are about 20 or 30 replacements.

That will filter the variable instance_ShortProductTitle

But next, I want to also do it for several other variables. This will take lots of copy and pasting. How would I do this using subroutines please?

oh really I can use that instead

I didn't know that it would make things better.

I will use Document Query Selector everywhere instead. I had no idea it made a difference! thanks!!

update on this -

the special filter is no longer required since using the Query Selector method to scrape the date - I can just do a standard filter

1 Like

Just looking at the Return from subroutine step for the first time.

What if I want it to always return the value to the source?

0 Subroutine example Macros.kmmacros (5.5 KB)

with


Screenshot 2024-02-23 at 16.19.58

You were able to put a space between local_Source and String? Does String need to be there? Is it creating a new variable?

From the Wiki, Manual, Variable Naming Rules:

  1. Variable names must start with a letter, and then can contain letters, numbers, spaces, or underscores.

local_Source String is a unique variable, different from local_Source


If you add a Return Result Action into a Subroutine it will always return the specified value as a result to the Execute Subroutine Action that called it. In the calling/returning end the value will be stored in the variable you define in the Execute Subroutine Action.

In your example subroutine macro you do not set any value to the local_Result variable, therefore it will return empty.

I am not sure what exactly you’d like to return, but if you where to specify your %Variable%instance_Title% in the Return Result Action, it would return that variables content, saving it in the calling end to the local_Result variable that you’ve specified in the Execute Subroutine Action.

EDIT: The checkbox in the Subroutines header field, the one labeled "Returning a value", needs to be checked for it to be true, that the Return Result always returns, of course

1 Like

woah variables can have spaces :exploding_head:
That breaks my brain for some reason
Thank you, I did not know that!

oh I seeeee, so it's it's to hand the value/calculation back to the thing that called it.

(btw I have searched YouTube for video tutorials on subroutines but no-one has covered it yet. I also sub to ScreenCastsOnline and they haven't done subroutines either.) (I am very much an audiovisual leaner)

local_Result was there from ComplexPoints' example.
I don't actually want to use that variable. I would ideally like the result be saved to the original variable.

(to recap: I am passing a string through a chain of filters, one by one. I don't want to create a new variable at any time.)

So, imagine this is Macro2 (to emojify instance_ShortProductTitle)

In the above screenshots (Macro2), the variable shown is instance_ShortProductTitle. It will be the same variable throughout the entire "chain".

Making Macro3 (to emojify the brand name - instance_BrandName)

Right now , I call Macro2 from Macro1. But in order to do it for more variables, I must duplicate Macro2 to make Macro3 ... and then laboriously go down and change every single action to the next Variable (e.g. instance_BrandName)

This is inefficient because if I tweak some of the filters in Macro2, I must then also make the exact same edits in Macro 3

Macro1 is the Main Macro (the others are just called from this one)

(BTW, if it's not clear Macro1 is the main macro. It is the one that will call Macro2, Macro3, Macro 4 etc)

Possible to do all this using subroutines?

i am trying to do this with my limited understanding.
This would stop me from having to make:

  • Macro 4 (emojify the next variable)
  • Macro 5 (emojify the varaible after that)
  • etc.

Each would be identical apart from the variable being filtered.

Building on what I wrote in my last reply, you should, in itself, be able to do what you ask here, if you:

  1. In the subroutine end set the Return Result Actions text field to %Variable%instance_Title% (to return this variables value)
  2. And in the calling/returning end setting the "Saving result to", in the Execute Subroutine Action, to the same %Variable%instance_Title%

I am not by my computer now, and without testing I must admit I am not experienced enough with instance-variables to be sure*, but I think the instance variable should “return” the value from the sub macro, also without the Return Result Action.


However, if I read your macro correctly, it looks to me like nothing of this would work, as you, in your calling macros Execute Subroutine Action, are defining your instance_Title (as well as variables instance_Brand and instance_Variant) to be overwritten by the empty value from the local_Source variable. Removing %Variable%local_Source% from these three text fields seems like a good thing to do, as you are not defining that variable anywhere. If you instead add %Variable%instance_Title% to the text field labeled “_Title” you would send the value of the instance_Title through to the Subroutine. (But as written above, if I understand instance-variables correctly, I think sending this value to the Subroutine was already done by defining the variable with the instance-prefix, making it shared with any subroutine within the same executing instance).


*not really as experienced with Subroutines as I’d like either, so someone must correct me if they spot anything wrong or lacking in my understanding/explanation, but I hope/believe what I’ve write above to be true

For context, the instance_Title is scraped from an open Amazon Item page somewhere (using JavaScript in front browser window action)

I want to be able to pass this string - or any string - typically a sentence long, but could be just a word, or a few words, through this chain of search-and-replace filters.

A different Amazon page could be loaded a few seconds later. So it needs to happen fast and smoothly.

Suppose instance_Title was this product name:

Light Bulb for Women

As it is passes down the 'chain' it is convereted to

Light 💡for Women

and then

Light 💡 ♀

This is due to the find-and-replace filters (using RegEx) that I've shown.

I don't want to introduce any new variable names if I can just save to source

There are two different approaches here:

  • Constant names, the values of which don’t change
  • Mutable names, which have one value at one stage, and then another later on.

Debugging typically involves tracking down a confusion about what the value of a mutable name really is at a given point.

Avoiding mutability, and using constants wherever possible, turns out, perhaps unexpectedly, to be a very good way of reducing run-time complexity, and keeping well clear of the debug tar-pits.

for future readers: Subroutine to Get The Last Line Containing Specific Text helped me get my head around subroutines a bit more.

Can someone please tell me how I can convert my macro here into a subroutine:

:lipstick::lipstick::lipstick::lipstick:.kmmacros|attachment (76.8 KB)

I'm guessing it's not structured the correct way to be a subroutine, i.e. it's not possible.. reasons:

  • i actually want my variable itself to be a variable
  • the result i am returning needs to be saved the source (i.e. Save result to would be the same as input)

What you have there is:

  1. a list of pairs [(fromPattern, toPattern)]
  2. one operation which is applied to each pair in succession (search replace in a given text)

A simpler architecture for it might be:

  • A single text of several lines in which each line contains such a pair (separated consistently, for example by a tab character)
  • a For Each action over a collection consisting of each line in the list of substitution pairs.

Your subroutine could receive two parameters:

  1. Pairs: A Keyboard Maestro %Variable% reference to a text containing one line for each substitution pair.
  2. Source: A Keyboard Maestro %Variable% reference to the source text.

and it could return an updated (search-replaced) copy of the source text.


To extract the two distinct parts of each one-line (fromPattern, toPattern) pair, you could use a Keyboard Maestro Variable Array, with a specified delimiter character, and a one-based index.

1 Like