Is there a way to make a macro shorter by using having KM change a variable automatically?

In some of my macros I have to execute the same action many times, but on different variables. The variables are all named the same but numbered sequentially, e.g. "Variable 1", Variable 2", "Variable 3", etc.

I'd like to be able to tell KM to repeat a group of actions x number of times, and have each iteration of the action be performed on the next variable each in turn. That way the actions could be in the macro only once. As it is, I write the same group of actions over and over... sometimes over 100 iterations and editing the macro gets very sluggish with the interface bogging down. Not to mention it would be way faster to create the macro!

This sounds like just the sort of use case the variables collection was made for. Here's one way you could go about this:

Keyboard Maestro Actions.kmactions (2.5 KB)
image

The specifics may vary depending on exactly what sort of stuff you want to do, but this should perform the same actions on every variable with the same name and a number automatically.

This looks exactly what I've been looking for — thanks so much! I'll check out the wiki and whatever other sources I can find to understand it deeper.

I've been experimenting with this for awhile, but haven't been able to find a way to do what I want. I want to copy cells in a spreadsheet each to it's own variable, for subsequent use in another macro. I currently have these variables named TempName 1, TempName 2, TempName 3, etc. up to TempName 120

What I'd like to do is create (or use the already created) variables in a loop. I'd specify how many times I want it to loop and it would:
copy cell A:1 to variable TempName 1;
copy cell A:2 to variable TempName 2;
copy cell A:3 to variable TempName 3;
etc. up to 120 iterations

TempName could be stored either as a variable or a clipboard, either would work for my purposes.

I would use a completely different approach. Here is a design pattern I often use:

  1. Copy all source data into one KM Variable
    • In this case each Excel row would be put on a separate line
    • Each cell on that row separated by a TAB (or other unique character)
  2. You can either save the data in a Global Variable or Write the Variable to a File and read it back when needed.
  3. When ready to process the data. use a For Each action with either a Lines In collection or a RegEx Substrings In collection to extract the data for each row/cell.

Questions?

That's great, thank you! That works for me — this will be helpful going forward.

1 Like

It is also possible, in case you are curious, to pass a variable name to a macro as part of its parameter string, but each time you use or change the variable within the macro you have to do a little bit of extra work. And the extra work is different if you are using a Global or Local variable.

Here's an example of how it might look if, for example, you wanted a macro to get the value of a variable and calculate its factorial and put the result back into the original variable:

image

You could call the same macro with "Variable2" or "Variable3" and it would perform the same set of actions using that variable, even though none of these variable names appear within the macro itself.

I don't use this approach very much because the extra work is a bit annoying, but it's possible. I use it only when I absolutely have to.

I am curious about that. I'd like to be able to create new variables as the result of actions within a macro. I still haven't figured that out.

Could you post your macro called "Factorial variable" so I could study it? I am trying to understand what you are telling me but I don't get it yet.

Thanks!

The factorial example I just cited above was for illustration only. I don't have such a macro, it was just an example to make a point. But I'll show you the gist of the solution here. And you can ask further questions. Maybe you can even write the factorial macro after my explanation.

The first step is being able to pass the name of the variable as a parameter. I think you know how to do that part. It's just by passing a regular string to a macro.

The second step, inside the macro, is to extract the parameter and put the parameter into a variable. I would typically do it like this for a simple case:

image

This is one way to get the value of the parameter and put it into a variable. Of course that step can be more complicated if you want to pass multiple data items to the macro. I usually pass two or three variables at a time, but for this example we will pass only one since that's simpler.

Now that you have the contents of the parameter, this step is optional, but you usually want to extract the contents of the variable by that name. Here's how:

image

This important step takes the value of the LocalParameter, which in your case might be "Variable1" and puts the contents of the variable "Variable1" into a new local variable called "LocalValue". Back in school we used to call this "dereferencing" a variable. That means taking the reference string "Variable1" and converting it to its contents.

So that's the first part of the battle, getting the contents of the variable inside the macro. Form this point on you use LocalValue as your "copy" of "Variable1". So instead of doing a factorial, let's just square the value because that's simpler:

image

Now for the slightly tricky part: returning the value to the original variable. At the end of your macro, in order to pass the final value of LocalValue back into the REAL variable, we have to use a special action. It looks like this:

image

It's worth noting that this action works on KM's local variables only. If your internal variables are global then the syntax will be a little different. I'm recommending that you use local variables for this type of work. But if you insist on using global variable inside your macro we can probably get you the modified syntax.

If you put the above four actions into a macro, let's call it "Square variable", then this is how you would call it:

image

When you run that last block of code outside the macro, it will create a global variable called MyNumber, pass the NAME of the variable to the Squaring macro, (note that it does NOT pass the value, just the name as a string), then when that macro returns the variable MyNumber has miraculously turned into the number 25.

This is a simple example. Sometimes I pass both a variable and a constant to a macro, perhaps like this:

image

That would require extra handling to separate the variable part of the parameter from the numerical value. Hopefully this gives you the tools you need to try it out for yourself.

By the way, once you master this, we can probably show you how you can pass macro names to macros, not just variable names! I'm working on that because I'm creating a mini-language that KM interprets and my language needs to be able to specify KM macros by name.

1 Like

The best way to use Sub-Macros (Macros called via Execute a Macro action) to operate on many different Variables is using Instance Variables .

Since Sub-Macros do not have the formal parameters and return of a script, we have to use Instance Variables (IV) instead.

Suppose you have a Sub-Macro that converts miles to KM. Then you would need:

  • Parameter 1: Miles -- we'll use IV Instance_Miles
  • Return: KM -- we'll use IV Instance_Return

So, in your MAIN Macro:

  1. Local_Miles1 is set from some source, read from some file, etc.
  2. Set Instance_Miles to Local_Miles1
  3. Call the Sub-Macro
    • It uses Instance_Miles
    • and sets Instance_Return
  4. Set Local_KM1 to Instance_Return

The IV exist ONLY between this execution instance of the Main Macro and its call to the Sub-Macro, so there is no danger of overwriting data from other macros or triggers of the same macro.

Make sense?

Prior to Instance Variables we had to do a lot of dynamic variable creation that was very complicated. Don't need it now.

1 Like

I think you are right that the method you indicate is the best method. Of course that does require 4 separate actions in the main macro. If the user wants to avoid those extra statements, then passing a variable as a parameter may be better.

Passing a Global Variable via the Execute Macro Parameter can be dangerous.
It would allow for multiple macros, and multiple instances of the Sub-Macro to overwrite the same variable, thus providing unpredictable, unreliable results.

I agree that asynchronous processing can be hazardous to one's program health. However there are many ways in which asynchronous programming can mess up software, not limited to this case of passing variable names as strings.

Thank you for taking the time to help me out on this. It is a fairly complicated process to create the new variables, to be honest, a little above my head at this point. I am eliminating the need for new variables in some of my macros and am starting to use the Lines In Collection which is something that I didn't know how to do before. KM is such a deep program with so much capability — there's still a lot that I don't know about it even though I use it a lot in my work. I'm learning, bit by bit!

I'm glad you are learning. I have lots to learn too. Even if you didn't understand what I wrote, others may visit this thread in the future and learn, so I'm happy about that. There are many features of KM that are completely over my head, so we are in the same boat.

2 Likes

I want to thank you again for this... I have been using the For Each a lot and my macros are much faster to write, modify, and troubleshoot.

1 Like