Checks if a value exists in an array

Propose new features and changes
stealzy
Posts: 91
Joined: 01 Nov 2015, 13:43

Checks if a value exists in an array

06 Sep 2016, 07:20

There are often used combine items into a single string instead of an array. You can check if a value exists in this "array" by searching in this string:

Code: Select all

stringArray := item1 . item2 . item3
IfInString, x, stringArray
; Another workaround:
(x ~= "MyRegExp1|MyRegExp2|MyRegExp3")
; Another workaround:
(x=a || x=b || ... || x=z)
But it is not simple way check this in real array. Some examples from another language:

Code: Select all

; js method indexOf()
array.indexOf(var)
; php func in_array()
in_array(x, array)
; python
if 'a' in ['a','b','c']:
    print "YES"
else:
    print "NO"
I make such function, but it is better have built-in method. Python method would be good.

Code: Select all

; Return array of keys if exist
HasValue(var, arr) {
	arrOfKeys := {}
	for key, value in arr
		if (value == var)
			arrOfKeys.Push(key)
	return (arrOfKeys.Length() = 0) ? false : arrOfKeys
}
Previous try, only for 'simple' arrays
Last edited by stealzy on 28 May 2018, 12:24, edited 12 times in total.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Checks if a value exists in an array

06 Sep 2016, 07:37

I use this.. HasVal

Code: Select all

HasVal(haystack, needle) {
	if !(IsObject(haystack)) || (haystack.Length() = 0)
		return 0
	for index, value in haystack
		if (value = needle)
			return index
	return 0
}

Code: Select all

arr := ["a", "b", "", "d"]

MsgBox % HasVal(arr, "a") "`n"    ; return 1
       . HasVal(arr, "e") "`n"    ; return 0
       . HasVal(arr, "d")         ; return 4
And for associative arrays you can use HasKey


There was a request in the archived forum too (https://autohotkey.com/board/topic/8400 ... ue-method/) back in 2012.
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Checks if a value exists in an array

06 Sep 2016, 09:33

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Checks if a value exists in an array

06 Sep 2016, 11:38

just me wrote:FYI: If Var In ...
HotKeyIt wrote:

Code: Select all

IsItemInList(item, list, del:=","){
	haystack:=del
	if !IsObject(list)
		haystack.= list del
	else
		for k,v in list
			haystack.= v del	
	Return !!InStr(del haystack del, del item del)
}
That looks horrible compared to jNizM suggestion.
stealzy
Posts: 91
Joined: 01 Nov 2015, 13:43

Re: Checks if a value exists in an array

06 Sep 2016, 12:33

It seems, my suggestion is still shortest and practical :=).
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: Checks if a value exists in an array

06 Sep 2016, 14:14

Helgef wrote: That looks horrible compared to jNizM suggestion.
jNizM suggestion only works for array objects, not string lists. HotKeyIt's works for both

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Checks if a value exists in an array

06 Sep 2016, 14:56

Obviously, that's why I posted here, since this was about arrays. And it really doesn't make much sense to me to have a function do both.
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Checks if a value exists in an array

06 Sep 2016, 16:03

This is rather a workaround for AutoHotkey v2, I have also updated to check array/object directly.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Checks if a value exists in an array

08 Sep 2016, 03:21

stealzy wrote:It seems, my suggestion is still shortest and practical :=).
It's not always about shortest code is the best solution.

Helgef wrote:
guest3456 wrote:
Helgef wrote: That looks horrible compared to jNizM suggestion.
jNizM suggestion only works for array objects, not string lists. HotKeyIt's works for both
Obviously, that's why I posted here, since this was about arrays. And it really doesn't make much sense to me to have a function do both.
+1 Helgef
and HasValue is everything you need to do this job
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
stealzy
Posts: 91
Joined: 01 Nov 2015, 13:43

Re: Checks if a value exists in an array

10 Sep 2016, 09:31

jNizM, my code return index of element, your code return 1. There is not necessary to check IsObjet() and .Length() in my case.
And there is no need aplly this function to string. Do you use .MaxIndex() to string? I'm not.
Associative arrays are not the subject of discussion.

So, I stand by my opinion.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Checks if a value exists in an array

10 Sep 2016, 10:32

stealzy wrote:jNizM, my code return index of element, your code return 1. There is not necessary to check IsObjet() and .Length() in my case.
And there is no need aplly this function to string. Do you use .MaxIndex() to string? I'm not.
Associative arrays are not the subject of discussion.

So, I stand by my opinion.
You can just return k instead of 1 in jNizM's function, so that's not really an argument.
jNizM's function is significantly faster than yours, even with the IsObject() and Length() checks, in the special case where you have an idea of where to start searching, yours might be good.
stealzy
Posts: 91
Joined: 01 Nov 2015, 13:43

Re: Checks if a value exists in an array

17 Sep 2016, 14:12

I rewrite it without slow .MaxIndex() method. There is not necessary to check IsObject() and Length() even in this case, because 0 return for non-object and zero-length object.

Code: Select all

x:=4, Arr:=[0, 4, 5, 7, 4, 4, "z"]
MsgBox % "x:=4, Arr:=[0, 4, 5, 7, 4, 4, ""z""]`nindexOf(Arr, x) = " indexOf(Arr, x) "`nindexOf(Arr, x, 3) = " indexOf(Arr,x,3) "`ngetArrOfMatches(Arr, x) = " ArrToStr(getArrOfMatches(Arr,x))

indexOf(Arr, var, fromIndex:=1) {
	for index, value in Arr {
		if (index < fromIndex)
			Continue
		else if (value = var)
			return index
	}
	return false
}

getArrOfMatches(Arr, var) {
	ArrMatch:=[], index:=0
	Loop
		if index := indexOf(Arr, var, index + 1)
			ArrMatch.Push(index)
		else
			Break
	Return ArrMatch
}

ArrToStr(array, depth=5, indentLevel="") {
	list := "["
	for k,v in Array {
		if (IsObject(v) && depth>1)
			list.= ArrToStr(v, depth-1, indentLevel) ", "
		else {
			if v is number
				list.= v
			else
				list.= """" v """"
			if (k != Array.MaxIndex())
				list.= ", "
		}
	}
	return list "]"
}
Last edited by stealzy on 16 Mar 2017, 10:14, edited 2 times in total.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Checks if a value exists in an array

19 Sep 2016, 04:14

stealzy wrote:I rewrite it without slow .MaxIndex() method. There is not necessary to check IsObject() and Length() even in this case, because 0 return for non-object and zero-length object.

Code: Select all

indexOf(var, Arr, fromIndex:=1) {
	for index, value in Arr {
		if (index < fromIndex)
			Continue
		else if (value = var)
			return index
	}
	return false
}
I don't mean to be a pain here, but the advantage of knowing where to start the search is mostly lost due to if (index < fromIndex) and in the case of fromIndex:=1 there is a tremendous amount of unnessecary checks: if (index < fromIndex). Your first function is fine when you have want to start the search "far enough" from the start of the array, otherwise jNizM's function is all you need. I agree the IsObject() and Length() checks are not nessecary, at least if it returns 0, it could return -1 so the user can distinguish between value not being in the array and inappropriate input.
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Checks if a value exists in an array

19 Sep 2016, 04:52

There is only a somewhat dubious benefit to the if (index < fromIndex) check: if true, the value = var comparison is avoided. Comparisons of very long strings can be costly, so there may be some specific cases where it helps. However, for the check to be useful, it must take less time than it saves. If the caller doesn't provide a fromIndex greater than 1 - or if the array contains only numbers or objects - no time will be saved by the check, so it will only hurt performance. That might also be true for short string comparisons.

If you remove that redundant part, the function is basically the same as jNizM's but without the IsObject and Length checks.

The Length check has the effect of avoiding the construction of an enumerator object when the function is given an empty array, so the function might perform better for such arrays. However, since the check itself has a cost, it will perform worse for the most common cases, where the array is not empty. In other words, it's not useful.

If you pass a non-object value to the for-loop, it will just do nothing, like Loop 0. However, haystack.Length() would throw an exception in v2 if haystack is not an object, so the IsObject check serves the purpose of avoiding that. Otherwise, it's pretty useless.

If you wanted to differentiate between "value not found" and "haystack not valid", it would be more efficient to do so after the for-loop:

Code: Select all

HasVal(haystack, needle) {
	for index, value in haystack
		if (value = needle)
			return index
	if !IsObject(haystack)
		throw Exception("Bad haystack!", -1, haystack)
	return 0
}
The Length check has another effect: inconsistent behaviour when the caller passes an associative array. For example:

Code: Select all

MsgBox % HasVal({key: "X"}, "X")  ; 0
MsgBox % HasVal({key: "X", 1: 0}, "X")  ; key
Without the Length check, the function would work just fine with associative arrays.
stealzy wrote:I rewrite it without slow .MaxIndex() method.
(I may be misinterpreting you.) MaxIndex is not the slow part - you're only calling it once per function call. The slow part is most likely looking up the array index on every iteration - i.e. Arr[A_Index + fromIndex - 1]. Using an enumerator avoids these lookups (but the enumerator itself has overhead, so in some cases it's not the best choice).
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Checks if a value exists in an array

19 Sep 2016, 05:46

thanks for clarification... added your suggestions and a few more examples: https://autohotkey.com/boards/viewtopic ... 17#p109617
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: Checks if a value exists in an array

19 Sep 2016, 18:05

Helgef wrote:Obviously, that's why I posted here, since this was about arrays. And it really doesn't make much sense to me to have a function do both.
so then why did you try to compare something thats not comparable?

lexikos wrote:

Code: Select all

HasVal(haystack, needle) {
	for index, value in haystack
		if (value = needle)
			return index
	if !IsObject(haystack)
		throw Exception("Bad haystack!", -1, haystack)
	return 0
}
if i wanted to add this as a method to all objects, how would i do it? the help page says the 'Default Base Object' is used for non-object values, so i'm guessing thats not what i would use..

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Checks if a value exists in an array

19 Sep 2016, 22:38

guest3456 wrote:if i wanted to add this as a method to all objects, how would i do it?
You can't. The function is not applicable to all objects, anyway.

You can define your own array/collection class and use that, and you can override []/{}.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Checks if a value exists in an array

20 Sep 2016, 07:23

guest3456 wrote: so then why did you try to compare something thats not comparable?
What is not comparable?

Return to “Wish List”

Who is online

Users browsing this forum: No registered users and 28 guests