Using an Array in ControlGetText? (Plus a Bunch of Object Q&A!) Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

20 Mar 2017, 11:59

@ lexikos Okay, upon further testing I am really confused. I tried the following code to test things out:

Code: Select all

$^m::
	testObj := {myVar1 : "test", myVar2 : "default", myVar3 : "random"}
	loop 12
		myArray[A_Index] := new testObj
	myString := JSON.Dump(myArray[3])
	MsgBox % myString
	return
The result is that the MsgBox displays an empty pair of quotes. However, if I do MsgBox % myArray[3].myVar1 it displays "test" as expected. And if I do this:

Code: Select all

$^m::
	testObj := {myVar1 : "test", myVar2 : "default", myVar3 : "random"}
	myString := JSON.Dump(testObj)
	MsgBox % myString
	return
This it displays "{myVar1 : "test", myVar2 : "default", myVar3 : "random"}", again, as expected.

So I am really not sure what I am doing wrong, and why testObj works but myArray[3] doesn't - even though myArray[3] clearly contains an instance of testObj since I can reference a member variable just fine.
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Using an Array in ControlGetText?

20 Mar 2017, 13:17

This defines your base object
testObj := {myVar1 : "test", myVar2 : "default", myVar3 : "random"}
This creates an instance of the base object
myobj := new testObj
The created instance has no variables ... the variables belongs to the base object ( this is why JSON.Dump() shows as empty )
When 'getting' myobj.myVar1 value, since its doesn't exist for the instance, it returns the base object value of that variable
See https://autohotkey.com/docs/Objects.htm#Meta_Functions
https://autohotkey.com/docs/Objects.htm#Meta_Functions wrote:When the script gets, sets or calls a key which does not exist within the target object, the base object is invoked as follows...
Try this ... ( Now that myVar1 exist for that instance, JSON.Dump() shows it. )

Code: Select all

testObj := {myVar1: "test", myVar2: "default", myVar3: "random"}
myobj := new testObj
myobj.myVar1 := "sometest"
myString := JSON.Dump(myobj)
MsgBox % myString
This is another reason why class are a much better way to define templates for your objects.
When calling new, you can assign the default values to the instance

Code: Select all

myobj := new MyObject
myString := JSON.Dump(myobj)
msgbox % myString

class MyObject {
	__new() {
		this.myVar1 := "test"
		this.myVar2 := "default"
		this.myVar3 := "random"
	}
}
Hope this helps !
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

20 Mar 2017, 13:37

Thank you, that exactly explains what I was seeing and what I was doing wrong. I definitely need to switch to using a class, yes.

So in effect __New is just a special function that is called when the class is instantiated, and can do anything, correct? And I guess __Call is a special function in the same way that is used when an instance of the class is... what? If I understand correctly, __Get would be invoked if you do something like Variable := myClass(), but I'm not sure when __Call gets used.

Is it possible to... I think the terminology is "overload an operator"? For example have a definition that says what to do if you write myValue := myVar + myClass()?
guest3456
Posts: 3453
Joined: 09 Oct 2013, 10:31

Re: Using an Array in ControlGetText?

20 Mar 2017, 13:58

MaxAstro wrote:upon further testing I am really confused. I tried the following code to test things out:

Code: Select all

$^m::
	testObj := {myVar1 : "test", myVar2 : "default", myVar3 : "random"}
	loop 12
		myArray[A_Index] := new testObj
	myString := JSON.Dump(myArray[3])
	MsgBox % myString
return
The result is that the MsgBox displays an empty pair of quotes.

There are two problems with your code:
  1. You are trying to assign values to myArray keys within your loop, but myArray isn't even an array. Its a random name that you pulled out of thin air that you didn't even define yet. Before you use it, you need to create an array/object if thats what you want to use it as:

    Code: Select all

    $^m::
    	myArray := []                ;pick one any one
    	;myArray := {}
    	;myArray := Array()
    	;myArray := Object()
    	testObj := {myVar1 : "test", myVar2 : "default", myVar3 : "random"}
    	loop 12
    		myArray[A_Index] := new testObj
    	myString := JSON.Dump(myArray[3])
    	MsgBox % myString
    return
    
    Now at least you get the correct JSON output: an empty object instead of an empty quoted string.
  2. Do you know what the new keyword does? If not, then why are you using it? If you want a copy of the object, then you should use the .Clone() method instead.

    Full working code:

    Code: Select all

    $^m::
    	myArray := []
    	testObj := {myVar1 : "test", myVar2 : "default", myVar3 : "random"}
    	loop 12
    		myArray[A_Index] := testObj.Clone()
    	myString := JSON.Dump(myArray[3])
    	MsgBox % myString
    return
    
No need for classes

https://autohotkey.com/docs/Objects.htm
https://autohotkey.com/docs/objects/Object.htm

MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

20 Mar 2017, 15:24

Ah, I was not aware that unlike normal variables, arrays have to be declared before they are used. Back when I did C++ I was very diligent about declaring absolutely everything in advance, but AHK not requiring variables to be declared has resulted in me developing some bad habits... >.>

I was using the new keyword because basically all I knew about objects was copying other people's code, and that is what the code I copied from used. ^^; I'm learning lots of stuff in this thread, though, so I'm super glad I asked.

Good to know that what I'm trying to do is possible with an object. I probably will use a class, though, because I think there are some helpful things I can do with the __New function.
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Using an Array in ControlGetText?

20 Mar 2017, 22:24

MaxAstro wrote:Ah, I was not aware that unlike normal variables, arrays have to be declared before they are used.
Do not compare an array to a "normal variable". An array is not a type of variable. You cannot declare an array any more than you can declare the number 2.

You could not use an array because you did not have an array to use. The solution was to *create* (not "declare") an array and assign it to the "normal variable".
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Using an Array in ControlGetText?

21 Mar 2017, 12:32

guest3456 wrote:You are trying to assign values to myArray keys within your loop, but myArray isn't even an array.
Good point !
guest3456 wrote:Do you know what the new keyword does? If not, then why are you using it?
Maybe he's trying to learn ?
Like maybe myArray[A_Index] := {myVar1 : "test", myVar2 : "default", myVar3 : "random"} would be better than wasting resources creating testObj variable and cloning its value ?
Maybe ...
guest3456 wrote:No need for classes
Class may not seem relevant in the above example but I can't see any downsides to using it.
I suggested classes to MaxAstro because he mentionned he had multiple function interacting with his objects.
MaxAstro wrote:The objects also need to be accessible to every function in my script, as lots of different functions interact with them in different ways.
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Using an Array in ControlGetText?  Topic is solved

21 Mar 2017, 13:38

A class is mainly composed of variables ( aka attributes ) and functions ( aka methods ) and some special subroutines.

__New is the constructor ( https://en.wikipedia.org/wiki/Construct ... ogramming) )
__Delete is the destructor ( https://en.wikipedia.org/wiki/Destructo ... ogramming) )

Each variable have read and write subroutines called getters and setters ( aka accessors and mutators ... https://en.wikipedia.org/wiki/Mutator_method )
__Get and __Set are special subroutines called before any variable specific getter and setter allowing advance control upon access to any variables.
MaxAstro wrote:but I'm not sure when __Call gets used.
__Call is also a special subroutines called before any of the class method/function.

That being said, you probably won't need any of those special subroutine ( __Call, __Get, __Set, __Delete ) for basic class usage.
MaxAstro wrote:Is it possible to... I think the terminology is "overload an operator"?
It probably is, but I never had to do it in AHK so I can't help you there.

I don't want to confuse you but you could try to analyse some of the class behavior yourself by running this code ...
Spoiler
Hope this helps !
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

21 Mar 2017, 16:15

@lexikos My turn to be pedantic - mostly because I really do want to learn the correct terminology. What "type" would a variable that holds an array be?

@4GForce Thanks. The various class subroutines are something that I just could not wrap my head around, even after reading the help files, but I think I am understanding it now. I'll play around with that code when I get the chance.
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: Using an Array in ControlGetText?

22 Mar 2017, 02:31

A variable does not change type depending on what kind of value it contains. It's exactly the same type of variable that it was before you assigned it the value, and it will remain exactly the same type of variable after you assign it a different type of value.

That's why I said
The solution was to *create* (not "declare") an array and assign it to the "normal variable".
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

22 Mar 2017, 09:27

Ah, okay. I had been assuming that AHK variables still had a type and just handled type changing behind the scenes. Interesting. Very different from what I am used to with C++. Thanks for the clarification.
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Using an Array in ControlGetText?

22 Mar 2017, 14:28

MaxAstro wrote:@4GForce Thanks. The various class subroutines are something that I just could not wrap my head around, even after reading the help files, but I think I am understanding it now. I'll play around with that code when I get the chance.
I'm glad if that helped, hopefully its going to help some other readers of this post.
There is also a basic class tutorial samardac made recently https://autohotkey.com/boards/viewtopic.php?t=6033
You could find some more interesting infos there.

Good luck !
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

22 Mar 2017, 15:39

@4GForce Running the code you provided is super helpful. A couple things are still happening that I don't understand.

1) After going through the initial __Set calls (interesting that even setting a key in __New still calls __Set, I wouldn't have guessed that and it would have driven me up a wall), it ends with "__Set(myVar4, something)". Immediately after that it displays "__Set(_myVar4, something)"; I am not sure what that means.

2) Towards the end, after displaying "somefunction(asdf)", it displays "__Get(IsArray)" followed by "__Call(_NewEnum, )" twice. Not sure what either of those mean.
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Using an Array in ControlGetText?

22 Mar 2017, 17:29

MaxAstro wrote:A couple things are still happening that I don't understand.
1) There are a few things going on there.
You probably noticed that MyVar4 define the getter and setter for the variable _myVar4 ...
As far as I'm concerned this is AHK specific since in other languages you would use the same name.
But doing so in AHK would result in an infinite loop ... as the getter would call itself to return the value. ( same thing with the setter )

Code: Select all

class MyObject {
	MyVar {			; <──┐
		get {		;	 │	
			Return this.MyVar
		}
	}
}
This is basically why __Get and __Set are called for both MyVar4 and _myVar4 since ...
myobj.myVar4 := "something" calls __Set(MyVar4, "something") then inside that setter definition the value is assigned to this._myVar4, so __Set is called again for _myVar4.
Then you would assume that MyVar4 is also an instance variable but it's not.
Did you notice that JSON.Dump() returns {_myVar4: "something"} and not {MyVar4: "something"} because _myVar4 is the actual variable.
I wish I could give more detailed explanation and proper terminology about this, but it also confuses me and the documentation doesn't mention anything about it ...
https://autohotkey.com/docs/Objects.htm ... s_property

2) Those come from JSON.Dump(myobj)
The Dump() function seems to be checking the object type ( __Get(IsArray) ) and then grabbing the variables thru an enumeration.
I would have to analyse the Dump() function to provide a more detailed explanation.

As for the _NewEnum method, you can find some information about it here https://autohotkey.com/docs/objects/Object.htm#NewEnum and here https://autohotkey.com/docs/objects/Enumerator.htm
Basically, _NewEnum can be overloaded to provide a custom way to enumerate thru your object.

Code: Select all

myobj := new MyObject
for k, v in myobj {
	; do something
}
The default enumerator will be used unless you define a custom one.

Code: Select all

class MyObject {
	_NewEnum() {
		return new CustomEnumerator()
	}
}
class CustomEnumerator {
	Next(ByRef key, ByRef value) {
		; enumeration logic
		return key
	}
}
Hope this explains it a bit !


I would really appreciate if someone with better AHK knowledge than me could provide some comments, examples or just point at some documentation I might have missed.
Thank you !
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

22 Mar 2017, 17:40

Okay, I gave this my first try, and... well, some things worked, and then I wrote code that literally crashes AHK. XD

I decided to organize things a little differently (basically an array of each type of value instead of an array of items), but I'm not adverse to changing that. In any case, I got the basics working; the part that is stumping me is assigning and retrieving data from the class. I though I would use a get/set property for that, but I'm not sure it will do what I need. Here is the code I have so far:

Code: Select all

class Items
{
	__New()
	{
		this.Barcode := []
		this.Brand := [] 
		this.Product := [] 
		this.PromoRetail := [] 
		this.RegularRetail := [] 
		this.Tax := [] 
		this.CRV := [] 
		this.Organic := [] 
		this.GlutenFree := [] 
		this.Size := [] 
		this.SizeType := []
	}
	
	TestSet()
	{
		this.Barcode[1] := 11111122222
		this.Brand[1] := "BRAND NAME"
		this.Product[1] := "PRODUCT NAME"
		this.PromoRetail[1] := 3.99
		this.RegularRetail[1] := 5.99
		this.Tax[1] := true
		this.CRV[1] := false
		this.Organic[1] := false
		this.GlutenFree[1] := true
		this.Size[1] := 10
		this.SizeType[1] := "OZ"
	}
	
	Product[ItemNum]
	{
		get {
			return { Slot : ItemNum, Barcode : this.Barcode[ItemNum]
				   , Brand : this.Brand[ItemNum], Product : this.Product[ItemNum]
				   , PromoRetail : this.PromoRetail[ItemNum], RegularRetail : this.RegularRetail[ItemNum]
				   , Tax : this.Tax[ItemNum], CRV : this.CRV[ItemNum]
				   , Organic : this.Organic[ItemNum], GlutenFree : this.GlutenFree[ItemNum]
				   , Size : this.Size[ItemNum], SizeType : this.SizeType[ItemNum] }
		}
		set {
			if value[Slot] := ""
				return
			else
			{
				this.Barcode[Slot] := value[Barcode]
				this.Brand[Slot] := value[Brand]
				this.Product[Slot] := value[Product]
				this.PromoRetail[Slot]:= PromoRetail[Barcode]
				this.RegularRetail[Slot] := RegularRetail[Barcode]
				this.Tax[Slot] := Tax[Barcode]
				this.CRV[Slot] := CRV[Barcode]
				this.Organic[Slot] := Organic[Barcode]
				this.GlutenFree[Slot] := GlutenFree[Barcode]
				this.Size[Slot] := Size[Barcode]
				this.SizeType[Slot] := SizeType[Barcode]
				return
			}
		}
	}
}
Now obviously the part that doesn't work is the Product[] property, because I really have no idea what I am doing there. xD The idea is that you could do something like myObject := Items.Product[1] to create an object that holds all of the information for product #1, and also do Items.Product[1] := myObject to take an object and store the appropriate product information in the class.

Buuut... I can't syntax, and I don't even really know if a get/set block is actually what I want there, or if you can even pass parameters to a get/set block like that...

Also among things I don't know: Does __New need to return? What about TestSet?

Thank you so much for your patience with me, I know I'm not the fastest learner ever. ^^;
4GForce
Posts: 553
Joined: 25 Jan 2017, 03:18
Contact:

Re: Using an Array in ControlGetText?

22 Mar 2017, 18:06

MaxAstro wrote:Does __New need to return?
Not in your case ... you don't have to override the new operator.
https://autohotkey.com/docs/Objects.htm#Custom_NewDelete wrote: Whenever a derived object is created with the new keyword [requires v1.1.00+], the __New method defined by its base object is called. This method can accept parameters, initialize the object and override the result of the new operator by returning a value.
Each instance of Items would only have 1 barcode ... so not an array. ( this also applies to the other attributes )
this.Product is also probably one of your main problem, read what I said above about getters and setters infinite loop.
I will give you further assistance with your code when I have more time.
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

23 Mar 2017, 09:29

Drat, I thought the New block was one of things I had down... if that's not the syntax for creating arrays within a class, then what is?
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

23 Mar 2017, 10:43

UPDATE: Got it working!! Get/set was the correct solution, I just had a bunch of syntax issues. Didn't need to use __New at all. And TestSet needed to return, that was causing the crash. Here is the working code:

Code: Select all

class Items
{
	Barcode := []
	Brand := [] 
	Product := [] 
	PromoRetail := [] 
	RegularRetail := [] 
	Tax := [] 
	CRV := [] 
	Organic := [] 
	GlutenFree := [] 
	Size := [] 
	SizeType := []
	
	TestSet()
	{
		this.Barcode[1] := 11111122222
		this.Barcode[4] := 21212121212
		this.Brand[1] := "BRAND NAME"
		this.Product[1] := "PRODUCT NAME"
		this.PromoRetail[1] := 3.99
		this.RegularRetail[1] := 5.99
		this.Tax[1] := true
		this.CRV[1] := false
		this.Organic[1] := false
		this.GlutenFree[1] := true
		this.Size[1] := 10
		this.SizeType[1] := "OZ"
		return
	}
	
	GetItem[ItemNum]
	{
		get {
			return { Slot : ItemNum, Barcode : this.Barcode[ItemNum]
				   , Brand : this.Brand[ItemNum], Product : this.Product[ItemNum]
				   , PromoRetail : this.PromoRetail[ItemNum], RegularRetail : this.RegularRetail[ItemNum]
				   , Tax : this.Tax[ItemNum], CRV : this.CRV[ItemNum]
				   , Organic : this.Organic[ItemNum], GlutenFree : this.GlutenFree[ItemNum]
				   , Size : this.Size[ItemNum], SizeType : this.SizeType[ItemNum] }
		}
		set {
			if (value.Slot == "")
				return
			else
			{
				this.Barcode[value.Slot] := value.Barcode
				this.Brand[value.Slot] := value.Brand
				this.Product[value.Slot] := value.Product
				this.PromoRetail[value.Slot]:= value.PromoRetail
				this.RegularRetail[value.Slot] := value.RegularRetail
				this.Tax[value.Slot] := value.Tax
				this.CRV[value.Slot] := value.CRV
				this.Organic[value.Slot] := value.Organic
				this.GlutenFree[value.Slot] := value.GlutenFree
				this.Size[value.Slot] := value.Size
				this.SizeType[value.Slot] := value.SizeType
				return
			}
		}
	}
}
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Using an Array in ControlGetText?

23 Mar 2017, 17:04

Just wanted to shoot out another thanks to everyone who helped me in this thread, especially lexikos and 4GForce. Just finished updating my (fairly large) script to use the Items class instead of a pseudoarray, and it's so much cleaner and easier to use now. Even figured out how to get the class to spit out items formatted in CSV format fairly easily, which makes another part of my job way easier.

Anyway, yeah. Thanks everyone. :)

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: inseption86, peter_ahk, Rohwedder, william_ahk and 207 guests