How to Write to a Position in an Array Variable

While KM doesn't fully support array variables, any variable with delimeters is sort of an array: You can reference an item in a position. So given the variable myTest with contents a,b,c, then...

%Variable%myTest[1]% = a, ...[2] = b, and ...[3] = c

The problem is you can only refer to these specific positions to read their values; you cannot write to a specific position—you can only write the full variable. So while these semi-arrays are great for storing lots of information in one spot, they're not so good if you want to update that information.

But there's actually a relatively simple way to do it, at least for small relatively static arrays. Given the same example from above, if I want to change a,b,c to a,foobar,c I can do so like this:

Set Variable myTest to text:
%Variable%myTest[1]%,foobar,%Variable%myTest[3]%

This seems obvious in hindsight, but I searched the forums and didn't find anything similar, and it took me a few minutes to reason through ... so I thought it might be useful for others. I wouldn't want to use this method for a 20-element array that's full of often-changing dynamic data, but for small occasional-use arrays, it works great.

-rob.

3 Likes

This is a nice insight. I can see this being useful for say, the coordinates of a window where Keyboard Maestro saves the four bits of data in a fixed order left, top, width, and height in a comma-delimited numerical string. Your idea would allow one coordinate of the four to be changed at a later stage, whilst leaving the others alone. I'm sure there are many other uses too.

Hey Rob,

That’s a nice, simple solution for the task you describe.

Here are some example macros for handling larger data-sets by field number. (The Perl macro jumped out as the fastest on my old Mojave system.)

-Chris


AppleScript:

Replace an Array Item by Position (AppleScript) v1.00.kmmacros (6.5 KB)

Macro-Image


JavaScript for Automation:

Replace Array Item (JXA) v1.00.kmmacros (6.6 KB)

Macro-Image


Perl (Faster than AppleScript or JXA):

Replace Array Item (Perl) v1.00.kmmacros (6.3 KB)

Macro-Image

Keyboard Maestro Export


1 Like

Two years on, I figure I should update this topic with the method I've been using, which is regular expression replacements, like this:

Keyboard Maestro Export

The above would replace the second item in the array, regardless of how long the array is—the expression captures the first field as $1, then doesn't capture the field to be replaced, and captures everything after that as $2. The replacement puts the first part back ($1), then the value I want to set, then the rest ($2), whether that's one, two, or ten more fields. Because I include the commas in the captures, I don't even have to type those.

This method has a huge advantage over the other in terms of flexibility: If I add an item to my array variable (as a new last item in the array), with this method I only have to modify any commands that set the previous end-of-element array value: They'd need to be modified to not capture everything into the last group. But all other calls could remain untouched, as they simply say "and the rest of the values" in their capturing regex.

It's also a heck of a lot faster to complete, in terms of not having to copy/paste multiple array formula references into the Set Variable field. Less readable, of course, but I'm fine with that.

-rob.

1 Like

Rob - as of KM 11 you can now both get and set variable array elements:

Haven’t tried it myself yet but I have a huge number of macros that would benefit from a rewrite just for this :smile:

1 Like

In KM 11, specifying \n as the array variable delimiter:

Update element at Variable Array index.kmmacros (2.7 KB)

4 Likes

Now I've tested this added KM 11 functionality and it works well. Here's my test macro:

Download Macro(s): Test Setting Array Elements.kmmacros (3.9 KB)

Macro-Image

Keyboard Maestro Export

Macro-Notes
  • Macros are always disabled when imported into the Keyboard Maestro Editor.
    • The user must ensure the macro is enabled.
    • The user must also ensure the macro's parent macro-group is enabled.
System Information
  • macOS 13.6.1
  • Keyboard Maestro v11.0.1d1

Example output:

image

Some points to note:

  1. Setting array element 0 (zero) causes the action to fail since element zero holds the array size.

  2. Setting an element beyond the size of the array extends the array to accommodate the new element. For example setting array element 15 of a 3-element array extends the size of the array to 15.

  3. Setting the element -1 of an array actually sets the last element. Setting the -2 element sets the next-to-last element. And so on...

  4. For an array of size N, setting any element -N-1, or -N-2, or -N-3 etc results in no change to the array and no error. I would have thought this should raise an error - @peternlewis ?

4 Likes

Excellent news, that's a very nice addition! But for backwards compatibility, I probably shouldn't start relying on it until I'm ready to make my macros KM11 only, right?

-rob.

1 Like

Spot on - that's why I won't be doing any rewrites quite yet until all my users have upgraded to 11.

Yes, it should be an error.

It will also be an error to set an index more than 100 past the end of the array.

3 Likes

When did KBM change RegEx syntax to use $1, $2, etc. instead of \1, \2, etc.?

Earlier this evening I was looking at a macro by @JMichaelTX, Check for duplicate lines in a variable and remove, posted June 23, 2020, which used the following RegEx syntax:

image

Reading that example, it was clear to me that it should have been

\1\2\3\4

But I did not realize that it should have been

$1$2$3$4

instead.

Was @JMichaelTX's post using the wrong syntax at the time, or did KBM change at some point since then?

I did not know about this alternate \1 notation either, but as per the wiki both notations are accepted syntax in KM:

For regular expressions, the replacement can refer to capture groups using either $1 or \1 notation (eg %Calculate%CHARACTERS(\1)% would be the count of characters in the first capture match (v8+)), or named capture groups (using (?<name>...) with the notation ${name}.

Link: Wiki, Search and Replace Action

But after KM8 the \1 notation can only be single digit (\1, \2, etc), as opposed to the $1 that can also be $23, $24, etc., the wiki says.

3 Likes

Thank you so much for finding those details!

1 Like

It took me a little while to get it working and at first I couldn't understand why it wasn't doing what I expected.

Here's the action:

And here's the variable it's supposed to be editing:

But it wasn't doing anything.

Then I noticed the \n after the array index in @ComplexPoint's example. But \n didn't work for me. I had explicitly created the list with %Return% characters and it took \r to match them.

When I got the \r delimiter specified in both places, it works.

I also noticed that, when I had the syntax right, I could use the "variable preview" line at the bottom to "inspect" the variable and verify that I was using the right index. In between my first attempt and my successful one, the list had increased by one item so the item I wanted to edit was now [11]\r instead of [10]\r.

Also, a trick I've become fond of: I added this action at the end of another macro that I'm working on and executed it with the Try button at the bottom of the Editor window. Now that I've tested it, I'll delete that action.