Requesting more options for MIN() and MAX()

Right now we need to use exactly two parameters for the MIN() and MAX() functions. If I want the MIN or MAX of more than two values, I have to nest these functions for as many times as I have values to test. (E.g., "MIN(MIN(1,2),3)" returns "1".) Perhaps that's okay up to ten values, but I would like these functions to support a single parameter that is the name of a variable that contains a list of numbers, eg.,

MIN(MyListofNumbers)

The above example should return the lowest number in that list of numbers. E.g., if the string contained "5,4,3,6,7" the result should be "3".

This would help me a lot, but it would be twice as helpful to create two new functions that return the index of the MIN and MAX values rather than the values themselves, e.g.,

INDEXMIN(MyListofNumbers)

The above example would return the index of the lowest number. For example, if that variable contained "5,4,2.77,1,3" the result should be 4.

I have workaround for these issues, but it would be nice to have these functions to simplify my code (and perhaps speed it up.)

I realize that only a small fraction of my requests ever make it into the KM product, but I'm still allowed to ask.

5 Likes

In the meanwhile, for:

... you can preface a list of arguments to Math.min and Math.max with the three-dot spread operator (in an Execute JS action).


e.g.

const xs = kmvar.local_List.split(",");

return Math.min(...xs)

Minima Maxima and their indices.kmmacros (6.6 KB)

I appreciate that workaround. I also have workarounds using shell scripts. Do you know what the KM Engine overhead is for "Execute Javascript for Automation" compared with "Execute Shell Script"?

In my experience, Shell Script seems to be faster...

I don't find that overheads, relative or absolute, impinge perceptibly on anything that I do.

Once we are reaching for scripts, my personal preference is for idioms that are slightly more typed (Haskell, Python, JS, for example).

( The "everything is a string" approach seems to cost a lot of diagnostic time, in practice – only too easy, and common, to save seconds and lose hours :slight_smile: )

That's great, but perhaps you don't have your macros running in an infinite loop, like I need to have. Every little bit of speed helps me.

In that (possibly rather rare ?) predicament, it might be fun to look at Haskell or Rust,
but in the meanwhile, Keyboard Maestro's %JSONValue% does, of course,
make it easy to return multiple computations from a single osascript instance.


Minima Maxima and their indices (multiple values in JSON).kmmacros (4.6 KB)

2 Likes

I asked ChatGPT if Rust or Haskell had native access to Apple's OCR (Apple's Vision Framework) or a fast Find Image function, and it said no. So I'll stick with Keyboard Maestro.

1 Like

This gets some love from me as a quality of life improvement, but these are simple enough to implement as native action subroutines following a brute force approach. And with a speed for a list of n items of around 3+n*4 milliseconds it's not that slow.

Example "max" that could easily be subbed:

MAX example.kmmacros (6.3 KB)

Image

2 Likes

I also very much support this request, but still just wanted to throw in a couple more workarounds — these using Apple Script as a shell for a Shell.

Firstly I wanted to use the sort -n function of the Shell (my goto numeric sort function), then picking first value as MIN, and last value as MAX.
Only after having figured this one out in an AS I realized that there probably was a way to set this up more directly using built in MIN-/MAX-functions of the Shell, and found a way to set up something that seems to work with support from a LLM.

Both are wrapped in Apple Scripts mostly to be able to write MIN and MAX-values to separate variables.

These are slower than the JS approach (that steadily runs at about 60 ms here on my system), but still not slow as both od these AS's run at about 200 ms here on my system, even with really long lists

MIN-MAX emulation — From commaseparated numberlist.kmmacros (5.7 KB)
(KM v11.0.4)

Macro image

1 Like

This thread shows a large variety of ways to solve a simple problem. So I like this thread.

Noted.

This is unlikely to happen. Use a loop to find the lowest value.

1 Like

For giggles, here's three versions of a subroutine to return max,maxIndex,min,minIndex for a list of numbers.

The most obvious way is to treat the list as an array and index through it:

...checking each element as you go:

SUB - Max and Min with Indexes.kmmacros (6.0 KB)

Image

Which, for me, takes ~10s for a list of 1,000 numbers.

But that seems slower than the number of actions in each loop would suggest -- perhaps the element referencing slows things down for large lists. And we know KM's text processing is blisteringly fast -- how about if we treat the list as text and "For Each" it

...and process each item in the resulting collection?

SUB - Max and Min with Indexes v2.kmmacros (6.8 KB)

Images

For me, ~4s for 1,000 numbers -- much better!

Of course, if it's a race we're running -- @ComplexPoint's JS will be the one to beat. Here it is, subbed to the same in/outputs:

SUB - Max and Min with Indexes (CP's JS).kmmacros (3.0 KB)

Image

...and, for a 1,000 item list, that takes... ~50 milliseconds!

At which point I hand CP the laurel crown and slink away in embarrassment...

The speed tester, if anyone wants to try for themselves:

Max-Min Tester.kmmacros (7.0 KB)

Image

2 Likes

That's amazing. And your analysis was also amazing. I'm guessing you are/were a professional programmer. In this case, I'm sold on Javascript. However to be honest, my requirements are not for 1000 numbers, just closer to 10 numbers, but in an infinite loop. So speed is a factor for me, but not because my list of numbers is large.