How to Count Characters with For Each

Goal: to count the Pipe "|" characters in a TriggerValue with the For Each Action

Happy Sunday!

I must be freakin' stupid...
I cannot get the For Each thing down....
Now there are for sure other ways to count the | character in a variable I'm sure.
But I want to learn how to do it with this (mind block) For Each.

The TriggerValue will be a single line ie. Why|Cant|I|Get|This

Finished Display On Screen Various Trigger Value K0339 copy.kmmacros (43 KB)

There are a couple issues with what you've done there. The most obvious is that the Set Variable that counts matches is incorrect: In a calculation field, you don't need %Variable%—just put in the name of the variable.

The second is that the For Each loop looks at lines, not characters within words. So the logic needs to be reworked, because your test won't do what you want it to do. We need to step through the characters, not the lines. Especially as you said it's always going to be one line.

Third, you have to set Local_Count to zero before you can start incrementing it.

With all that said, here's a solution that works—there may be slightly simpler ways using a For Each, but this one seems to do what you want:

Finished Display On Screen Various - rg.kmmacros (34 KB)

(Note that I was using e|e|e|e|e as a test trigger; you'd have to modify to integrate in your macro, obviously.)

Now, with all that said, this is definitely not the way I would do this :). I'd use the shell, where there are about 300 ways to do this rapidly; here's one I found on StackExchange years ago and kept around as a favorite shell script action:

$ echo "Why|Cant|I|Get|This" | tr -cd '|' | wc -c | tr -d ' '
4

The echo command outputs the string, which is then sent to the second with (ironically) the pipe sysmbol. The tr command is used to translate or delete characters—the c option means "not the following character" and d is "delete." So basically, this deletes everything except the pipe symbol. That's passed to wc, which uses the -c option to count characters. But that output contains leading spaces, which the final tr takes care of.

And in Keyboard Maestro parlance for your string, a one-action shell script will spit out the answer:

echo "$KMVAR_ValueCombo" | tr -cd '|' | wc -c | tr -d ' '

Not my work at all, but very useful.

-rob.

Well as always, thank you.

Me and the For Each just don't get along....
I would have thought there would have been an easier approach using the For Each, but I am wrong.
Thank you for the detailed explanation and the alternate solution. All good....

Lastly a question regarding:

I thought that Local variables were by nature zeroed out at the beginning of the execution of a macro that contains them.
I work a lot in Filemaker Pro and I may be getting confused with the local variables in that, which are zeroed out upon new execution of scripts that contain them.

ERG - NOTE: Just looked it up in the wiki.... case sensitive...local.... so right. - Obviously I'm just starting to get into using local variables.

With your help and @Nige_S, @ComplexPoint and @DanThomas (forgive me for anyone I have not mentioned - this forum is priceless) I've been chewing on slimming down my macro library end eliminating old/unused/un-needed variables.

Have a Blessed Sunday
Cheers

No variables, including global, exist until they're set. If you're setting them to text, that's not a problem. Or if you're setting them to a number, that's not a problem.

But if you're setting one to a value plus the variable's own value, the problem is that it doesn't exist until it's set. That's why you'll get an error if you try to do that.

Ergo, if you're going to use one as a running sum, you first have to set it to zero—doesn't matter if it's a local, instance, or even global variable: It has to exist with a value before you can add something to its current value.

The wiki points out that local is case insensitive—Local or local doesn't matter. My main point was that in a calculation field, you can't include %Variable%.

-rob.

1 Like

You can use a "For Each" action:

Count Pipe (For Each).kmmacros (4.3 KB)

Image

But consider that your text is also a pseudo array with the delimiter | -- it will have one more element than the number of |s in the text, and you can get the number of elements of an array by referencing its 0th element. So:

Count Pipe (Array).kmmacros (3.4 KB)

Image

For your use here, this is probably the way to go. After all, why do you care about the number of |s in the trigger? Isn't the more important thing the number of elements in the |-delimited trigger string?

There may even be more logical ways of handling that trigger string -- probably by treating it as an array -- depending on what you are doing.

For fun -- you could also delete every character that isn't a |, then get the length of the resulting string:

Count Pipe (S'n'R).kmmacros (3.5 KB)

Image

...which I've just realised is the KM version of @griffman's shell script. So, in an effort to make up for that, here's the reverse of his method where you find just the | characters:

Count Pipe (Shell).kmmacros (3.7 KB)

Image

3 Likes

You can also count the pipe characters in a variable/text like this, very short:

image

1 Like