Match Closest Value

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
jc0r
Posts: 32
Joined: 27 Nov 2015, 07:09

Match Closest Value

17 Nov 2019, 04:57

Hi all

Im looking for a function similar to lookup but in ahk. I would like to provide a value, say: 3.72 and then have the script lookup in a range i provide for example

3.65
3.7
3.75

and return the closest value. In this case, 3.7

I don't want to use an external file to contain the lookup range if possible, i would like to have it in the script. Anyone know how i would go about this?
jc0r
Posts: 32
Joined: 27 Nov 2015, 07:09

Re: Match Closest Value

17 Nov 2019, 06:03

Many thanks for that. Do you know if there is a way to achieve this without using loops as speed is key for me.

Something like

Code: Select all

array := [1, 2, 3, 4]
MsgBox, "match 1.2"(array*)
Giving the result 1? Also i need to be able to specify the result values, i cant just round up or down to the nearest whole number because sometimes i will face scenarios like this

Code: Select all

array := [1.71, 1.72, 1.73, 1.74]
MsgBox, "match 1.7312"(array*)
I basically have an array with 350 numbers in, and i need to provide a value and find the closest match in that array of 350
Gully
Posts: 18
Joined: 14 Nov 2019, 11:07

Re: Match Closest Value

17 Nov 2019, 06:28

jc0r wrote:
17 Nov 2019, 06:03
Many thanks for that. Do you know if there is a way to achieve this without using loops as speed is key for me.

Something like

Code: Select all

array := [1, 2, 3, 4]
MsgBox, "match 1.2"(array*)
Giving the result 1? Also i need to be able to specify the result values, i cant just round up or down to the nearest whole number because sometimes i will face scenarios like this

Code: Select all

array := [1.71, 1.72, 1.73, 1.74]
MsgBox, "match 1.7312"(array*)
I basically have an array with 350 numbers in, and i need to provide a value and find the closest match in that array of 350
You're going to need loops, whether or not the thread linked provided the most efficient solution or not I don't know. But you surely need to loop through the entire array, you can't just magically find the closest one without checking them all.
User avatar
Chunjee
Posts: 1501
Joined: 18 Apr 2014, 19:05
Contact:

Re: Match Closest Value

17 Nov 2019, 09:33

Hi there. I checked out the linked threads and found the solutions there unsatisfactory. They're not encapsulated in functions so they're not even portable without legwork

Here is what I came up with:

Code: Select all

fn_findClosestNumber(param_number,param_array) {
    canidateArray := []
    for Key, Value in param_array {
        element := {}
        element.param_number := Value
        element.difference := abs(param_number - Value)

        if (canidateArray[canidateArray.Count()].difference > element.difference || canidateArray.Count() == 0) {
            canidateArray.push(element)
        }
    }
    return canidateArray[canidateArray.Count()].param_number
}

Code: Select all

msgbox, % fn_findClosestNumber(1.5, [3,4,1.8,0.2])
; => 1.8
msgbox, % fn_findClosestNumber(1.5, [1.4,0.2,2])
; => 1.4
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Match Closest Value

20 Nov 2019, 22:33

One thing I find useful for this sort of 'is a value within a range' task, is to use an intermediate array.
You round the needle value down, and look up the rounded value in the intermediate array.
By using the rounded value, you can jump to a relevant place in the list.

Code: Select all

array:
1 1.23
2 1.41
3 1.59
4 2.21
5 2.47
6 2.67
7 4.15
8 4.58
9 4.96
10 5.18

intermediate array:
1 1 [1.23]
2 4 [2.21]
4 7 [4.15]
5 10 [5.18]
e.g. key/value '5 10', the smallest value in the list where
the integer part is 5, is the 10th item in the list
Here's some code:
Some of it is a little fiddly, so do notify of any issues.

Code: Select all

;==================================================

w:: ;generate random numbers
vOutput := ""
Loop 20
{
	;vNum := Random(0.0, 10.0)
	Random, vNum, 0.0, 10.0
	vOutput .= Format("{:0.2f}", vNum) "`r`n"
}
Sort, vOutput, % "D, N"
Clipboard := vOutput
MsgBox, % "done"
return

;==================================================

q:: ;find nearest values
vList := "1.23,1.41,1.59,2.21,2.47,2.67,4.15,4.58,4.96,5.18,6.17,6.30,6.99,7.81,8.00,8.33,8.68,8.72,8.77,9.48"
oArray := StrSplit(vList, ",")

;generate intermediate table:
oMap := Object()
vNumLast := ""
Loop Parse, vList, % ","
{
	vNum := Floor(A_LoopField)
	if !(vNum = vNumLast)
		oMap[vNum] := A_Index
	vNumLast := vNum
}

;list intermediate table:
;e.g. key/value '5 10', the smallest value in the list where
;the integer part is 5, is the 10th item in the list
vOutput := ""
for vKey, vValue in oMap
	vOutput .= vKey " " vValue " " oArray[vValue] "`r`n"
MsgBox, % "lookup table:`r`n" "k v a[v]`r`n" vOutput "`r`n"

;find closest match:
vOutput := ""
Loop 21
{
	vNum := (A_Index-1)*0.5
	vNum2 := Floor(vNum)
	Loop
	{
		if (vNum2 < 0)
		{
			vIndex := 1
			break
		}
		if oMap.HasKey(vNum2)
		{
			vIndex := oMap[vNum2]
			break
		}
		vNum2--
	}
	if !(vIndex = 1)
		vIndex--
	vDiff := Abs(vNum-oArray[vIndex]), vIndex2 := vIndex
	Loop
	{
		vIndex++
		if !oArray.HasKey(vIndex)
			break
		vDiff2 := Abs(vNum-oArray[vIndex])
		if (vDiff2 >= vDiff)
			break
		vDiff := vDiff2, vIndex2 := vIndex
	}
	vOutput .= vNum " " oArray[vIndex2] "`r`n"
}
MsgBox, % "number and closest match:`r`n" vOutput
return

;==================================================
Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bing [Bot], Lpanatt and 313 guests