Object.HasValue/Contains/KeyOf/FindValue

Discuss the future of the AutoHotkey language
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Object.HasValue/Contains/KeyOf/FindValue

13 Apr 2017, 17:15

This mightn't be strictly for v2, but it may be a replacement for if var in MatchList.

It was also suggested and discussed on the old forum, and recently in the v2-thoughts topic.
IN/CONTAINS

Has anybody considered a normal function for this? E.g. StrContains, ArrayContains, ObjContains.
I wrote:Arr.HasValue(Val) is not much different to Val in Arr, maybe even clearer. That would eliminate the (unasked?) question of how or whether Val in Obj should be handled when Obj isn't an array (even though there's currently no clear distinction between arrays and other objects). In JavaScript, "A" in {A: 1} is true and "A" in ["A"] is false. Using HasKey() or HasValue() makes it clear, and also provides a way to specify case sensitivity that doesn't involve changing a thread-global setting.
If Arr.HasValue(Val) is implemented, should it always return a boolean value?

When it finds the value, it also finds the key. Returning the key would be more useful, but then {0: "A"}.HasValue("A") and {"": "A"}.HasValue("A") would be false. Even if it's called something like FindValue or KeyOfValue, it doesn't seem correct to "reserve" the empty string to mean "not found".

Maybe Object.HasValue(Value [, CaseOrTypeSensitive, ByRef Key])...

I like the name HasValue for symmetry with HasKey, but it I'm not sure it would be appropriate if the method has more functionality similar to InStr (i.e. StartingKey and Occurrence). On the other hand, those parts are probably not needed often and can already be done with a for-loop.
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Object.HasValue/Contains/KeyOf/FindValue

13 Apr 2017, 17:48

Definitely Object.HasValue(Value [, CaseOrTypeSensitive, ByRef Key]) which returns true or false.

If we have CaseOrTypeSensitive for value, for consistency we would need it for HasKey as well, probably .HasValue(Value [, ByRef Key]) would be sufficient?
Instead add separate Methods for CaseOrTypeSensitive, probably HasKeyExact(Key) and HasValueExact(Value [, ByRef Key]).
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Object.HasValue/Contains/KeyOf/FindValue

13 Apr 2017, 23:32

If we have CaseOrTypeSensitive for value, for consistency we would need it for HasKey as well, probably .HasValue(Value [, ByRef Key]) would be sufficient?
No on both points.

Objects treat "a" and "A" as the same key, so it makes no sense to have a case-sensitive mode for HasKey(). If it was possible to make keys case-sensitive, then HasKey() should always be either case-sensitive or case-insensitive depending on whether the object is case-sensitive.

HasValue() is different; values can be anything at all. There is no reason to restrict the search to case-insensitive (or case-sensitive) matches. By default the behaviour should be like = or InStr(). InStr has a parameter for case-sensitivity, not a separate function InStrExact().

Ideal parameter order is not as clear. Maybe Key should be the second parameter, or maybe it's more intuitive for CaseSensitive to follow the value, as in InStr().
guest3456
Posts: 3453
Joined: 09 Oct 2013, 10:31

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 01:19

if a byref key variable is passed, and the key was empty string "", then querying this var after the call would return an empty string, which might lead the user to think that it was never assigned a return value. but then i again i guess if HasValue returned true, then you would know it..

what about returning the key directly and having a note in the docs to be careful when using it as a boolean because obviously key 0 or "" would look like its false? but then what would you return if it was notfound?

does it make sense to disallow 0 or "" as obj keys? might be going too deep down the rabbit hole now

another question: if multiple keys have the same value, which key gets returned to the byref param? i guess the first in the enumeration order, but how does that go when the obj is an assoc array with mixed keys of both strings and ints?

User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 01:42

I disagree with HotkeyIT and Lexikos.
ByRef Values are a makeshift way of programming that is pretty much against the point of any modern Syntax.
When you need to do ByRef ( unless it is for passing binaries ( which are also makeshift to me ) ) it actually means that you tried to get too much information out of 1 function/method. Which means you need to split it.
So I suggest splitting the method into 2 functions.
Recommends AHK Studio
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 03:15

guest3456 wrote:if a byref key variable is passed, and the key was empty string "", then querying this var after the call would return an empty string, which might lead the user to think that it was never assigned a return value.
Who's going to write code like this?

Code: Select all

object.HasValue(value, key)
if key
   ...
If a user writes that and gets confused, I know where I'd place the blame. The right way to write it is (in my opinion) also the most obvious way (and that's the point):

Code: Select all

if object.HasValue(value, key)
    ... use key ...
what about returning the key directly and having a note in the docs to be careful when using it as a boolean because obviously key 0 or "" would look like its false?
That would be more likely to confuse. To clarify the result, one would have to do something like if (key != "" || object[key] = value), or to be more robust, if (key != "" || object.HasKey(key) && object[key] = value), if the value could be "". It would be making both the user and the code do more work when the method could have just returned a clear result in the first place.

I had (re)considered adding an undefined value, but even with that, one has to remember to write if object.HasValue(value) != undefined rather than the more obvious and semantically correct (according to the meaning of the name, not the behaviour) if object.HasValue(value). (if object.FindValue(value) != undefined might be less confusing, but since if object.FindValue(value) would work most of the time, it's still a trap someone will fall into.)
does it make sense to disallow 0 or "" as obj keys?
No. Anyway, strings that convert to the number 0 are also considered false.
another question: if multiple keys have the same value, which key gets returned to the byref param?
The first one found, the same as if you used a for-loop. (In other words, the key lowest in the sort order, which is not guaranteed by the documentation.) One would not use the method when both 1) the object contains duplicate values and 2) it matters which key is returned; because there's no way to get the second key with that value (unless there are additional parameters like I mentioned in my first post).
i guess the first in the enumeration order, but how does that go when the obj is an assoc array with mixed keys of both strings and ints?
You can run a for-loop and find out. ;)
nnnik wrote:When you need to do ByRef ( unless it is for passing binaries ( which are also makeshift to me ) ) it actually means that you tried to get too much information out of 1 function/method.
I'm not sure whether to say "you're overgeneralising" or "that's just nonsense". Are functions only allowed to have a single scalar value as a result now? So we should have to query the mouse/control/window's position twice to get both coordinates, for instance?

There are many cases where the result(s) of some operation cannot be represented by a single value. Output parameters are used quite frequently in many common languages (generally those which lack multiple return values), obviously including AutoHotkey.

In this particular case (HasValue/Contains/KeyOf/FindValue, not "has this value" specifically), it is wasteful to search for a value within the object and then return incomplete information when it is found. With only a single return value, either we get a clear answer ("yes, the object has this value" or "no, it doesn't") and have to duplicate the work to get full information, or we get an ambiguous answer (such as "" which could be a key or mean "no value found") and we have to query the object again to clarify. If you want an inefficient solution, you can stick with the existing user-defined functions.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 04:23

lexikos wrote:
nnnik wrote:When you need to do ByRef ( unless it is for passing binaries ( which are also makeshift to me ) ) it actually means that you tried to get too much information out of 1 function/method.
I'm not sure whether to say "you're overgeneralising" or "that's just nonsense". Are functions only allowed to have a single scalar value as a result now? So we should have to query the mouse/control/window's position twice to get both coordinates, for instance?

There are many cases where the result(s) of some operation cannot be represented by a single value. Output parameters are used quite frequently in many common languages (generally those which lack multiple return values), obviously including AutoHotkey.
You seem to forget that we do have Objects and Arrays within AutoHotkey now. It makes sense to group a returned size since they describe the same elemnt and have the same scaling context etc... We don't do so for historical reasons ( also makeshift ). Many of these languages are not modern either and don't support modern syntax - something that I hope we will see in AutoHotkey, as progress might just pass us and leave us behind. As you might have already noticed I'm heavily taking side with a more object based approach.
lexikos wrote:In this particular case (HasValue/Contains/KeyOf/FindValue, not "has this value" specifically), it is wasteful to search for a value within the object and then return incomplete information when it is found. With only a single return value, either we get a clear answer ("yes, the object has this value" or "no, it doesn't") and have to duplicate the work to get full information, or we get an ambiguous answer (such as "" which could be a key or mean "no value found") and we have to query the object again to clarify. If you want an inefficient solution, you can stick with the existing user-defined functions.)
Yes I agree everything you said is right but there is a lot more that can be said here.
If we compare the action of "checking whether an array/object contains a value" to the action of "getting the key where the value is stored", we see that there is a fundamental difference, in the model, of the array/action, we want to take.
In one we are absolutely sure that the array/object contains the value we just don't know where yet. On the other hand we want to check if the value is inside there or not. Those are 2 different things that just happen to correlate implementation wise.
These differences in the models should lead to differences in the implementation.
When specifying them in your program and being implemented correctly into the language you can get better results e.g. it can help with debugging ( e.g. if we create 2 functions hasValue and keyOf the second could throw an exception if Value doesn't exist within Array. )
As for your 2 steps for 1 action problem: I don't know why you think that it is always necessary to use both actions together. Most of the times I either want to use 1 and mostly it is to find the key of a value, therefore I don't quite understand the second part of that quote. ( Also how the user implements something often makes more difference than whether it is executed in AutoHotkey level or the MCode level ( e.g. combine 2 arrays with object pointers to create fast references ) ).
I'm not even totally against the idea of the hasValue methos being implemented that way. I'm just against it being the only method to get a values key - as it is wasteful in my opinion.

edit: fixed some language
Recommends AHK Studio
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 06:15

We don't do so for historical reasons
Wrong. I don't do so because I prefer not to. Returning an associative array with fixed elements and returning information via output parameters both have benefits and drawbacks. There generally have to be more than a few parameters (like FileGetShortcut) before I consider using an object.
Many of these languages are not modern either and don't support modern syntax
What languages? The main ones I was thinking of were C++ and C#, both of which are widespread, mature and well-developed, but are still evolving. I don't know how you define "modern", but any language features added in the last few years surely must qualify. C++ and C# have both supported objects forever, and yet output parameters haven't fallen out of use. In any case, I don't think this line of discussion is relevant.
If we compare the action of "checking whether an array/object contains a value" to the action of "getting the key where the value is stored", we see that there is a fundamental difference, in the model, of the array/action, we want to take.
You have no idea what concepts the objects or arrays in every other user's scripts will represent, or the thought processes of those users, and neither do I. What you're proposing is only catering to the two ideas you've thought up, while being more restrictive and less efficient. What about, for instance, "finding the location of a value which may or may not exist"? That would be analogous to "indexOf" in various languages. Having used indexOf many times, it seemed obvious to me from the beginning that such a method should be able to tell you both whether a value was found and at what index.
I don't know why you think that it is always necessary to use both actions together.
I never said any such thing.

Do you insist that it is always unnecessary to find both whether a value exists and what its index/key is at the same time? Because your suggestion excludes that possibility.
I'm just against it being the only method to get a values key - as it is wasteful in my opinion.
Wasteful? Wasteful would be increasing code and documentation size to effectively duplicate a method. Wasteful would be searching an array (perhaps a very large portion of it) twice because hasValue() and getFirstIndexOfValue() are separate methods; or being forced to use exception handling to detect what is really a valid condition within the program.
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 06:48

How about to return an array containing all keys.

Code: Select all

for i,key in Object.HasValue("MyValue", true)
	  MsgBox % key
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 07:59

HotKeyIt wrote:How about to return an array containing all keys.
I like the idea:

Code: Select all

Object.HasValue(Value[, CaseOrTypeSensitive, ReturnKeys := False]
If ReturnKeys is False, the search will be stopped at the first match and the return value will be boolean.
If ReturnKeys is set to True, the search will be performed over all keys and an array of all matching keys will be returned, if any; otherwise False.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 08:42

This conversation has gone in a nad direction. I don't want to fight over this small details with you. Also what I want to say now has become a lot more clear to me.
The function you suggested has three ways it is going to be used in 3 ways ( 3 non hackish straight beautiful ways whatever you may call it ( I'm just trying to point out that those are not all use cases ) ).

Code: Select all

if obj.hasValue( value ) ;nr 1
if obj.hasValue( value, firstKey ) ;nr 2
obj.hasValue( value, firstKey ) ;nr 3
( The if isn't actually necessary but is there to highlight the fact that the return value is boolean )
The first thing I noticed was that you optimise for the case where an array might or might not contain such a value and if it contains the value return the only key this value is at.
The reason why I felt bad about this solution is entirely different though.
You are right we can not know how a user wants to code their programs and this solution does not cover all use cases of course it does.
Arrays/Objects are one of AutoHotkeys core features and I think we need to offer a little flexibility.

I know that implementing ad documenting these functions is hard work, but I would help with that ( I have been wanting to help for a while now ).
I'm just having some problems with setting up AutoHotkey so a little help with that is all I need.
Recommends AHK Studio
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 12:35

just me wrote:Object.HasValue(Value[, CaseOrTypeSensitive, ReturnKeys := False]
+1
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 13:19

just me wrote:Object.HasValue(Value[, CaseOrTypeSensitive, ReturnFlag := False])
I think that adding more flags is a good idea as this approach also supports the return all value keys. What I don't like is that my personally most used case isn't in there anywhere - the case where I want to return only the key of the first time this value is encountered. So I would still like to add a second method.
Recommends AHK Studio
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 13:24

So we could do:Object.HasValue(Value[, CaseOrTypeSensitive, ReturnFlag]) where ReturnFlag can be:
  • 0/false to return true/false
  • true / 1 / 2 ... to return only the first or multiple keys as array
  • -1 to return all keys as array
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 13:29

I thought of that too. That still wouldn't eliminate the surrounding array.
Recommends AHK Studio
guest3456
Posts: 3453
Joined: 09 Oct 2013, 10:31

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 13:41

nnnik wrote:I thought of that too. That still wouldn't eliminate the surrounding array.
0 = bool
1 = first key as string
-1 = all keys as array

?

User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 13:52

I'll take back everything I said. Looking over AutoHotkey v2 it seems that the changes I want don't seem to fit in there yet or maybe at all.
Maybe I expected too much.
Recommends AHK Studio
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 14:15

guest3456 wrote:0 = bool
1 = first key as string
-1 = all keys as array
That would bring us back to the problem with 0 and ""!
guest3456
Posts: 3453
Joined: 09 Oct 2013, 10:31

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 17:28

HotKeyIt wrote:
guest3456 wrote:0 = bool
1 = first key as string
-1 = all keys as array
That would bring us back to the problem with 0 and ""!
crap you're right :(

i prefer Lex's original over returning an array but either is fine i guess

if the intent is simply to replace "in/contains", then those previously never were able to check for multiple entries, so returning all keys that match might be overkill anyway

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

Re: Object.HasValue/Contains/KeyOf/FindValue

14 Apr 2017, 19:44

Thanks for the ideas.

Perhaps the best approach is to always return boolean, and if there's demand for additional functionality, a parameter or method can be added later.
nnnik wrote:I thought of that too. That still wouldn't eliminate the surrounding array.
Appending [1] would. ;)

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 22 guests