[Function] Min/Max including with Associative Arrays

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

[Function] Min/Max including with Associative Arrays

04 Dec 2017, 14:46

[Function] Min / Max

I have had a Min/Max function in my library for years that used a variadic parameter so that you could pass it as many parameters as you liked. This allowed it to also work with a simple array parameter if followed by a * but I discovered today that it did not work for associative arrays so I made a small tweak.

Code: Select all

Min(X:="�", List*)	; X := chr(0xffff)
{
	for key, element in List
		if (element < X)
			X := element
	return X
}

Max(X:="", List*)
{
	for key, element in List
		if (element > X)
			X := element
	return X
}
Credits to Helgef.

The Min function requires Unicode encoding of the file as it uses a special extended character.

If that is a problem this ANSI encoding function can be used.

Code: Select all

Min(List*)
{
	List._NewEnum().Next(,X)
	for key, element in List
		if (element < X)
			X := element
	return X
}
This uses the _NewEnum().Next to get the starting value for comparison in an associative array which makes this function slower than the one above.

Examples of usage:

Code: Select all

; Basic Usage
MsgBox	% Min(2,-11,4,3)	; -11

SimpleArrayofNumbers := [2,-11,4,3]
MsgBox	% Min(SimpleArrayofNumbers*)	; -11

SimpleArrayofText := ["cherry","apple","pear","banana"]
MsgBox % Min(SimpleArrayofText*)		; apple

SimpleArrayofMixed := [2,1,"Tree","Pear",3]
MsgBox	% Max(SimpleArrayofMixed*)		; Tree

AssociativeArray := {"one":"cherry","two":"apple","three":"pear","four":"banana"}
MsgBox % Max(AssociativeArray*)		; pear
Important to realize that alpha characters are greater than numbers.

Even thought Min/Max functions are fairly simple I figured these were worth posting as the ability to find the Min or Max value in an array is a pretty common need. These are also the fastest functions I could find that handled all the cases these functions can handle.

There can be 1 or more parameters that can be literals, variables, simple arrays, or associative arrays.

FG
Last edited by FanaticGuru on 11 Dec 2017, 14:17, edited 1 time in total.
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [Function] Min/Max including with Associative Arrays

04 Dec 2017, 18:43

Thanks for sharing FanaticGuru. :thumbup:
The ability to find the Min or Max value in an array is a pretty common need.
Ragnar has submitted a suggestion for built-in functions :arrow: github :thumbup:. It doesn't include string compairison but is variadic.

This gave me an idea for a new puzzle :arrow: Code Puzzle Thread (Puzzle 8).

Cheers.
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Function] Min/Max including with Associative Arrays

11 Dec 2017, 14:32

Updated: 2017 12 11

Improved the performance of the functions in first post with the help of Helgef.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [Function] Min/Max including with Associative Arrays

12 Dec 2017, 06:58

Hello FanaticGuru.
I have a few comments. I realised, that there is less than ideal behaviour of the Min(X:="?", List*) if you pass Min([]*) :(. The Max version is ok.
Also, I haven't really used the feature of variadic calls in other cases than with linear arrays, that is, [x,y,...] rather than {keyX:valX, keyY:valY,...}. The documentation says
•Numbering of parameters within the source array begins at 1.
So it seems we miss numeric keys < 1 when we expand AssociativeArray* :o, example,

Code: Select all

Min({0:1, 1:2}*) ; 2 :(
In v1, this is OK Min({"0":1, 1:2}*), but not in v2. Hence, for associative arrays, to be certain, one would need to pass the array rather than expand* it. Alternative function definition could be, Min(List) and the either call it like Min(array) if you have an array from the beginning, or Min([var1,var2]) if you want to pass variables. Or use multiple functions depending on the situation. Slightly sad.

Cheers.
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Function] Min/Max including with Associative Arrays

12 Dec 2017, 17:58

Helgef wrote:In v1, this is OK Min({"0":1, 1:2}*), but not in v2. Hence, for associative arrays, to be certain, one would need to pass the array rather than expand* it. Alternative function definition could be, Min(List) and the either call it like Min(array) if you have an array from the beginning, or Min([var1,var2]) if you want to pass variables. Or use multiple functions depending on the situation. Slightly sad.
Yes, it appears associative arrays with numerical keys less than 1 are a problem.

Here is an attempt at a function that can handle this case as wells as allowing the mixing of all kinds of data structures.

Code: Select all

MsgBox % MinX({0:1, 1:2}, {-1:-5,-5:0},-9,[-2,5,-19],-27,{-89:-101})
MinX(ByRef List*)
{
	X:="�"
	for key, element in List
		if IsObject(element)
		{
			for key2, element2 in element
				if (element2 < X)
					X := element2
		}
		else
			if (element < X)
				X := element
	return X
}
It is relatively quick when passing an array but when just sending two numbers it is going to be slower. Any improvements or comments are appreciated.

I am thinking in my library I will have:
Min that works great except with associative arrays with numerical keys less than 1.
MinArray that expects an array and handles as quickly as possible.
MinX that can handle a mix but might not be as quick as the two above.

Who would have thought a versatile and quick Min function would be so complicated.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [Function] Min/Max including with Associative Arrays

13 Dec 2017, 06:21

I am thinking in my library I will have:
It seems like this is what one has to do :think: .
Who would have thought a versatile and quick Min function would be so complicated.
Not me, thank you for bringing it up :ugeek: .

Note, Byref doesn't do anything with variadic parameters as far as I know.

Cheers.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [Function] Min/Max including with Associative Arrays

13 Dec 2017, 06:40

- Which functions in AHK v1/v2, currently receive arrays? Is it just StrSplit? (Note: some functions accept arrays passed with *, but that's not what I'm interested in here.)
- With StrSplit, when you specify multiple delimiters via an array, it appears to check all numeric keys (including zero/negative integers), but not string keys.
- I think that generally it's better to ignore string keys, and perhaps better to only check positive integer keys. You just have to state that that's how the function works. I.e. at present, for general functions e.g. mathematical functions, I would generally write functions that only check positive integer keys. For object functions I would/wouldn't support all keys, depending on the situation, sometimes I might make two versions for linear/associative arrays. Also, sometimes I might make two functions to handle recursion or not.

Code: Select all

q:: ;test how StrSplit handles string/numeric keys
vText := "_a_b_c_d_e_"
(oArray := {}).InsertAt(-2,StrSplit("abcde")*)
MsgBox, % oArray[-2]
MsgBox, % oArray[2]
oArray2 := StrSplit(vText, oArray)
MsgBox, % oArray2.Length()

vText := "_a_b_c_d_e_"
oArray := {x:"a"}
oArray2 := StrSplit(vText, oArray)
MsgBox, % oArray2.Length()

oArray := oArray2 := ""
return
When you pass an object to a variadic function, the variance function appears to retrieve all of the keys, except for non-positive integer keys, which is interesting behaviour. So you get curious results if you do a for loop, however, if you use a loop based on oParams.Length(), you get the behaviour that I would hope to get.

Code: Select all

w:: ;test how variadic functions handle string/numeric keys
(oArray := {}).InsertAt(-2,StrSplit("abcde")*)
MsgBox, % oArray[-2]
MsgBox, % oArray[2]
oArray.10 := 10
oArray.x := "x"
oArray.y := "y"
oArray.z := "z"
MyVdcFunc(oArray*)
return

MyVdcFunc(oParams*)
{
	vOutput := ""
	for vKey, vValue in oParams
		vOutput .= vKey " " vValue "`r`n"
	MsgBox, % vOutput

	vOutput := ""
	Loop, % oParams.Length()
		vOutput .= A_Index " " oParams[A_Index] "`r`n"
	MsgBox, % vOutput
}
[EDIT:] I have added to the variadic functions code a little bit.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
BartmanEH
Posts: 23
Joined: 01 Apr 2021, 15:48

Re: [Function] Min/Max including with Associative Arrays

17 Apr 2021, 08:07

FanaticGuru wrote:
04 Dec 2017, 14:46
[Function] Min / Max
I have had a Min/Max function in my library for years [...] but I discovered today that it did not work for associative arrays so I made a small tweak.

Code: Select all

Min(X:="�", List*)	; X := chr(0xffff)
{
	for key, element in List
		if (element < X)
			X := element
	return X
}

Max(X:="", List*)
{
	for key, element in List
		if (element > X)
			X := element
	return X
}
Credits to Helgef.
The Min function requires Unicode encoding of the file as it uses a special extended character.
I’m trying to understand why you start with a special extended character at all. Why not simply start with the value of the first element passed into the Min/Max function?
User avatar
FanaticGuru
Posts: 1906
Joined: 30 Sep 2013, 22:25

Re: [Function] Min/Max including with Associative Arrays

19 Apr 2021, 13:46

BartmanEH wrote:
17 Apr 2021, 08:07
FanaticGuru wrote:
04 Dec 2017, 14:46
[Function] Min / Max
I have had a Min/Max function in my library for years [...] but I discovered today that it did not work for associative arrays so I made a small tweak.

Code: Select all

Min(X:="�", List*)	; X := chr(0xffff)
{
	for key, element in List
		if (element < X)
			X := element
	return X
}

Max(X:="", List*)
{
	for key, element in List
		if (element > X)
			X := element
	return X
}
Credits to Helgef.
The Min function requires Unicode encoding of the file as it uses a special extended character.
I’m trying to understand why you start with a special extended character at all. Why not simply start with the value of the first element passed into the Min/Max function?

An optional parameter's default value must be a static value. It cannot be a dynamic value like a variable.

This approached is used so that the function can accept both array and variadic parameters.

Code: Select all

; Array parameter
Array := ["Apple", "Pear", "Orange"]
Min(Array)

; Variadic parameter
Min("Apple", "Pear", "Orange")

As shown in my Original Post, you can assign the first value to X like you suggest like this:

Code: Select all

Min(List*)
{
	List._NewEnum().Next(,X)
	for key, element in List
		if (element < X)
			X := element
	return X
}
But that is slower and the point was to make the function as efficient and quick as possible while still be versatile enough to handle array and variadic parameters.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 128 guests