objects: ordered array (function syntax to method syntax)

Get help with using AutoHotkey and its commands and hotkeys
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

objects: ordered array (function syntax to method syntax)

06 May 2017, 06:11

If anyone can help me perfect this. I'm trying to convert this ordered array class from function syntax to method syntax. Thanks for any help with this.

They should both give the same results.

[AHK_L] For Loop in order of key-value pair creation - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/6179 ... -creation/

Btw where does the 'i' come from in the oaEnumNext function?

Code: Select all

q:: ;ordered array (function syntax and method syntax)
Loop, 2
{
	if (A_Index = 1)
		obj := OrderedArray()
	else
		obj := new OrderedArray

	obj.one := 1
	obj.two := 2
	obj.three := 3
	obj.four := 4
	for k,v in obj
		t .= k "=" v "`n"
	t .= "`n"
	MsgBox, % t
}
return

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

OrderedArray()
{
	static base := Object("__Set", "oaSet", "_NewEnum", "oaNewEnum")
	return Object("_keys", Object(), "base", base)
}
oaSet(obj, k, v)
{
	obj._keys.Insert(k)
}
oaNewEnum(obj)
{
	static base := Object("Next", "oaEnumNext")
	return Object("obj", obj, "enum", obj._keys._NewEnum(), "base", base)
}
oaEnumNext(e, ByRef k, ByRef v="")
{
	if r := e.enum.Next(i,k)
		v := e.obj[k]
	return r
}

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

class OrderedArray
{
	__New()
	{
		return Object("_keys", Object())
	}
	__Set(k, v)
	{
		this._keys.Insert(k)
	}
	_NewEnum()
	{
		return Object("obj", this, "enum", this._keys._NewEnum())
	}
	Next(e, ByRef k, ByRef v="")
	{
		if r := e.enum.Next(i,k)
			v := e.obj[k]
		return r
	}
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: objects: ordered array (function syntax to method syntax)

06 May 2017, 12:40

If I were you, I'll start with thinking about what is going on in that __new

Edit: I gave it a try, I added a Push function too, maybe that is weird? :crazy:

Code: Select all

class OrderedArray
{
	__New() {
		this.__keys:=[]
	}
	Push(p*){
		; Maybe push isn't wanted, at least not with this beaviour
		for k, v in p
			this[this.MaxIndex()+1]:=v
		return this.MaxIndex()
	}
	__Set(k, v){
		if !InStr(",__keys,__nextInd,", "," . k . ",")
			this.__keys.push(k)
	}
	_NewEnum(){
		this.__nextInd:=1
		return this
	}
	Next(ByRef k, ByRef v:=""){
		k:=this.__keys[this.__nextInd]
		v:=this[k]
		return this.__keys.HasKey(this.__nextInd++) ; Edit <--
	}
}
Finally, what is your goal?

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

Re: objects: ordered array (function syntax to method syntax)

06 May 2017, 14:46

@Helgef. Btw I want to be able to use any string as a key name, so I think that this requires Object() aka {}, not Array() aka [].

I'm trying to convert the class from one syntax to another. I had hoped/expected this could be done with the method-based class looking pretty much identical to the function-based class.

==================================================

Goals: secondary 'reference'/'auxiliary' objects within classes.

One thing I'm not sure about is if it's possible to create additional objects within a class that all methods can refer to, or whether you are limited to one object within a class, and thus would have to use subkeys of that object to assist you.

In a worst-case scenario, perhaps I could have a global object, that all objects that I create from a custom class could use. And keep each object's content in a subkey of the global object, perhaps deriving the subkey's name from each object's address offset.

==================================================

Goals: enumerators.

Enumerators have been giving me the most trouble regarding objects, unfortunately of the few straightforward examples I've found, they relate to integers, or use function syntax, or have other issues like the 'i' variable in the example here, where I'm unsure where it came from.

==================================================

Btw.

Two key bits of knowledge about objects, that I only fully realised were true yesterday, and that I think are the main obstacle holding everybody back:

__Get / __Set / __Call only take place for a key that does not yet exist. Thus ideally, the key *never* exists, and you can take advantage of the __Get/__Set/__Call meta-functions for special purposes, and you use a secondary object if possible, to store everything.

Objects
https://autohotkey.com/docs/Objects.htm#Meta_Functions
Meta-functions define what happens when a key is requested but not found within the target object.
__Set:
- If there is a 'return' line (it doesn't matter what is returned), this prevents AHK from automatically adding 'key := value' to the object.
- Trying to set a new key's value within a __Set method, will trigger __Set, potentially resulting in an infinite loop. Use a function such as ObjRawSet to bypass this and manually add in a key.

==================================================

Goals:
- acquire the last bits of knowledge I need to complete my objects tutorial (including converting between function syntax and method syntax):
jeeswg's objects tutorial (not yet complete) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=29232
cf. my RegEx tutorial:
RegEx handy examples (RegExMatch, RegExReplace) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=28031
- a class where a key name that looks numeric is treated as a string
- 4 types of array: CSA CSD CIA CID (case (in)sensitive, keys returned in alphabetical/creation date order)
CIA would be a standard AHK object (plus treat all key names as strings, never numeric, plus have a count keys method)
CIS is basically the ordered array
CSD is basically a Scripting.Dictionary object
CSA would use a Scripting.Dictionary object
see:
case sensitive/case insensitive arrays (maintain key order, no key autosort) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=29198
- add a count method to custom classes to state how many keys it has

Other goals:
- a custom class that works like a normal class (as a basis for slight modifications to the standard AHK object)
- a class that returns items in reverse alphabetical order
- a class that returns items skipping some (e.g. get every nth item)
- a class where each item can have both children and its own value (like a treeview control)
- a class where you can return an object's parent

Other object tutorial goals:
- tips on converting code using COM.ahk's functions to AHK's native COM
- tips on converting code from other programming languages to AHK's native COM

Basically I want to stop spending time researching AHK's objects, so that I can move onto and finish other AHK and AHK-related projects.
Last edited by jeeswg on 07 May 2017, 01:47, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: objects: ordered array (function syntax to method syntax)

06 May 2017, 15:36

jeeswg wrote:@Helgef. Btw I want to be able to use any string as a key name, so I think that this requires Object() aka {}, not Array() aka [].
No, it is ok, and you are not limited to string keys either ;)
jeeswg wrote: One thing I'm not sure about is if it's possible to create additional objects within a class that all methods can refer to, or whether you are limited to one object within a class, and thus would have to use subkeys of that object to assist you.
In a worst-case scenario, perhaps I could have a global object, that all objects that I create from a custom class could use. And keep each object's content in a subkey of the global object, perhaps deriving the subkey's name from each object's address offset.

I'm not sure I follow this... I'm sure I do not follow this.
jeeswg wrote: Enumerators have been giving me the most trouble regarding objects, unfortunately of the few straightforward examples I've found, they relate to integers, or use function syntax, or have other issues like the 'i' variable in the example here, where I'm unsure where it came from.

See Enumerator object,
Next wrote: OutputVar1 Receives the key in a key-value pair.

Cheers!
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: objects: ordered array (function syntax to method syntax)

06 May 2017, 19:10

Try this. Note: Delete does not support FirstKey, LastKey and InsertAt and RemoveAt are not implemented because they don't make sense for OrderedArray!

Code: Select all

testObjects := Object(1, Object(), 2, new OrderedArray())
for i,obj in testObjects
{
    obj.one := 1
    obj.two := 2
    obj.three := 3
    obj["four","b"]:=1
    obj["four","a"]:=2
    obj.Push("c",2)
    for k,v in obj{
        t .= k "=" 
        if IsObject(v){
          for k,v in v
            t.= "`t" k "=" v "`n"
        } else t.= v "`n"
      }
    t .= "`n`n"
}
MsgBox 0,Compare,% t
t:=""
clone:=testObjects.2.Clone()
clone.Delete("three")
clone.Push("d")
clone.six:=4
for k,v in clone{
  t .= k "=" 
  if IsObject(v){
    for k,v in v
      t.= "`t" k "=" v "`n"
  } else t.= v "`n"
}
MsgBox 0,Clone,% t
moa:=new MyOrderedArray
moa.x:=1,moa.i:=2,moa.b:=3,moa.Push("a")
moa.MyFunc()
Class MyOrderedArray extends OrderedArray{
  MyFunc(){
    for k,v in this
      t.= k "=" v "`n"
    MsgBox 0,% A_ThisFunc,% t
  }
}
Class OrderedArray {
  __New(obj:=0, keys:=0){
    static objects:=[]
    if objects.HasKey(obj)
      return objects[obj]
    else if keys
      return objects[obj]:=keys
    objects[this]:=Object()
  }
  __Call(func,p*){
    ; Define prototype object for custom enumerator:
    static base := Object("Next", OrderedArray._EnumNext)
    if (func="_NewEnum") ; Return an enumerator wrapping our _keys array's enumerator:
      return Object("obj", this, "enum", (new OrderedArray(this))._NewEnum(), "base", base)
    else if (func="Push"){
      obj:=new OrderedArray(this)
      if !idx:=ObjMaxIndex(this)
        idx:=0
      for k,v in p
        this[++idx]:=v
      return ObjLength(this)
    } else if (func="Pop"){
      return ObjDelete(this,(new OrderedArray(this)).Pop())
    } else if (func="Delete"){
      key:=p.1
      for k,v in obj:=new OrderedArray(this)
        if (v=key){
          ObjDelete(obj,k)
          break
        }
      return ObjDelete(this,key)
    } else if (func="MaxIndex")
      return ObjMaxIndex(this)
    else if (func="MinIndex")
      return ObjMinIndex(this)
    else if (func="Length")
      return ObjLength(this)
    else if (func="SetCapacity")
      return ObjSetCapacity(this,p*),ObjSetCapacity(new OrderedArray(this),p*)
    else if (func="GetCapacity")
      return p.MaxIndex()?ObjGetCapacity(this,p*):ObjGetCapacity(this)
    else if (func="GetAddress")
      return ObjGetAddress(this,p*)
    else if (func="HasKey")
      return ObjHasKey(this,p*)
    else if (func="Clone")
      return obj,new OrderedArray(obj:=ObjClone(this),ObjClone(new OrderedArray(this)))
    fun:=(new OrderedArray(this))[func]
    return %fun%(p*)
  }
  __Set(k, v*){
    ; If this function is called, the key must not already exist.
    ; Just add this new key to the array:
    (new OrderedArray(this)).Push(k)
    if v.MaxIndex()>1{
      ObjRawSet(this, k, obj:=new OrderedArray)
      value:=v.Pop()
      return obj[v*]:=value
    }
    ; Since we don't return a value, the default behaviour takes effect.
    ; That is, a new key-value pair is created and stored in the object.
  }
  _EnumNext(ByRef k, ByRef v=""){
    ; If Enum.Next() returns a "true" value, it has stored a key and
    ; value in the provided variables. In this case, "i" receives the
    ; current index in the _keys array and "k" receives the value at
    ; that index, which is a key in the original object:
    if r := this.enum.Next(i,k)
        ; We want it to appear as though the user is simply enumerating
        ; the key-value pairs of the original object, so store the value
        ; associated with this key in the second output variable:
        v := this.obj[k]
    return r
  }
}
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: objects: ordered array (function syntax to method syntax)

07 May 2017, 03:53

Thank you both for your comments, I will check over them.

@Helgef. Re. additional objects:

Note: I would rather keep that secondary object *within* the class, and not use a global object.

Code: Select all

q:: ;test 'auxiliary'/'secondary'/'storage'/'additional' object
oStorage := {}
obj1 := new MyObj
obj1["a"] := "A"
MsgBox, % obj1["a"]
MsgBox, % oStorage[&obj1, "a"]
MsgBox, % obj1["a"]
return

class MyObj
{
	__Get(oParams*)
	{
		;MsgBox, % "[GET]"
		global oStorage
		Loop, % oParams.MaxIndex()
			oParams[A_Index] := "" oParams[A_Index]
		return oStorage[&this, oParams*]
	}
	__Set(oParams*)
	{
		;MsgBox, % "[SET]"
		global oStorage
		vValue := oParams.Pop()
		Loop, % oParams.MaxIndex()
			oParams[A_Index] := "" oParams[A_Index]
		oStorage[&this,oParams*] := vValue
		return
	}
}
==================================================

Unfortunately I misread the definition for __Call:

I thought it was this (incorrect):

Code: Select all

class ClassName {
    __Get([Key, Key2, ...])
    __Set([Key, Key2, ...], Value)
    __Call(Name, [Key, Key2, ...]) ;[INCORRECT]
}
but it is actually this:

Code: Select all

class ClassName {
    __Get([Key, Key2, ...])
    __Set([Key, Key2, ...], Value)
    __Call(Name [, Params...])
}
And so that scuppers a lot of the ideas I had for special classes. Or otherwise they would be more complicated to write.

==================================================

Btw what exactly are the differences between Array()/[] and Object()/{}?

Is this the answer?

Differentiating Between Array and Object - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/9530 ... nd-object/
Objects and arrays are exactly the same thing in AutoHotkey, there is no difference. [a, b, c] is just syntax sugar for {1: a, 2: b, 3: c}.
Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: objects: ordered array (function syntax to method syntax)

07 May 2017, 05:14

@HotkeyIt, interesting.
@jeeswg, re:re. I'm equally confused, however, I can live with it. ;)
Regarding, []/{}/Object()/array(), I'd say the only (perceived) difference is the input parameters. Once the object has been created, there is no way of knowing how it was created.
Spoiler
Cheers.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: objects: ordered array (function syntax to method syntax)

07 May 2017, 05:42

If I want to have more control of everything that goes on in a class, and do clever things with it, then in theory, I would like to be able to monitor every time something is done to a key. __Get/__Set/__Call only occur for a key that doesn't already exist in the main object. However if I store keys/values in a secondary object, rather than the main object, then keys are never found in the main object and __Get/__Set/__Call will be called every time anything is done to a key.

Unfortunately __Call's input parameters do not include a key's ancestor keys unlike for __Get and __Set, and thus cannot be used to retrieve a key's ancestor keys, it would appear.

Re. Array()/[] v. Object()/{}. The only advantage so far that I can see for Array() is that it makes certain lines slightly shorter.

Code: Select all

q:: ;Array() v. Object()
obj1 := ["red","yellow","green","blue"]
MsgBox, % obj1.3
obj2 := {1:"red",2:"yellow",3:"green",4:"blue"}
MsgBox, % obj2.3
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: objects: ordered array (function syntax to method syntax)

07 May 2017, 06:23

jeeswg wrote:If I want to have more control of everything that goes on in a class
I tried to get back to topic in this example, while still commenting on the off topic :D

Code: Select all

q::
	oJee:=new jeeOrderedMonitor
	oJee.bJee:="JeeB"	; B
	oJee.aJee:="JeeA"	; A
	oJee.cJee:="JeeC"	; C
	Msgbox, % "oJee.Haskey(.):`n`naJee:`t" oJee.HasKey("aJee")  "`toJee.aJee`t=`t" oJee.aJee "`nbJee:`t" oJee.HasKey("bJee")  "`toJee.bJee:`t=`t" oJee.bJee "`ncJee:`t" oJee.HasKey("cJee")  "`toJee.cJee:`t=`t" oJee.cJee
	s:="For loop:`n`n"
	for k, v in oJee
		s.=k  "`t" v  "`n" 
	msgbox, % s  "`nCheers"
return

class jeeOrderedMonitor {
	__new(){
		ObjRawSet(this,"data",[])
		ObjRawSet(this,"order",[])
	}
	__Set(k,v:=""){
		; inspect k, v
		passed:=true
		if passed
			this.data[k]:=v, this.order.push(k)
		return
	}
	__Get(k){
		return this.data[k]
	}
	; Another enum example:
	_NewEnum(){
		return new jeeOrderedMonitor.jeeEnum(this) ; Edit, there was a space here, it didn't seem to matter though, ...Monitor .jeeEnum(.)
	}
	class jeeEnum{
		__new(ref){
			this.i:=1
			this.ref:=ref
		}
		Next(Byref k, ByRef v){
			k:=this.ref.order[this.i]
			v:=this.ref.data[k]
			return this.ref.order.haskey(this.i++)
		}
	}
}
Cheers.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: objects: ordered array (function syntax to method syntax)

07 May 2017, 06:43

My main goal in this topic is achieving:
- 4 types of array: CSA CSD CIA CID (case (in)sensitive, keys returned in alphabetical/creation date order)

However, it seems so awkward to create custom classes that resemble anything that I would want to use, that I may consider giving up on the idea of custom classes entirely. I'd be interested to know if anyone has done anything useful with custom classes (apart from generating integers), including retrieving the parent of an object.

[EDIT:] Any good custom classes could be mentioned/linked to in my tutorial.

Btw the 'treeview' class idea, where each object can have a value *and* children, is not personally important to me.

Anyhow here are examples regarding 4 types of array, that don't use a custom class:

Code: Select all

;q:: ;list line frequency
vText := "q,W,e,Q,w,E"

;ARRAY TYPE 1: CIA, case insensitive, alphabetical order
;ARRAY TYPE 2: CID, case insensitive, date creation order
;ARRAY TYPE 3: CSA, case sensitive, alphabetical order
;ARRAY TYPE 4: CSD, case sensitive, date creation order

;ARRAY TYPE 1: CIA, case insensitive, alphabetical order
obj := {}
Loop, Parse, vText, % ","
	if obj.HasKey("" A_LoopField)
		obj["" A_LoopField]++
	else
		obj["" A_LoopField] := 1
vOutput := ""
for vKey, vValue in obj
	vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput

;ARRAY TYPE 2: CID, case insensitive, date creation order
obj := {}, obj2 := {}
Loop, Parse, vText, % ","
	if obj.HasKey("" A_LoopField)
		obj["" A_LoopField]++
	else
		obj["" A_LoopField] := 1, obj2.Push("" A_LoopField)
vOutput := ""
for _, vValue in obj2
	vOutput .= vValue " " obj[vValue] "`r`n"
MsgBox, % vOutput

;ARRAY TYPE 3: CSA, case sensitive, alphabetical order
obj := ComObjCreate("Scripting.Dictionary")
vText2 := vText
Sort, vText2, D, CS
Loop, Parse, vText2, % ","
	obj.item("" A_LoopField) := 0
Loop, Parse, vText, % ","
	obj.item("" A_LoopField)++
vOutput := ""
for vKey in obj
	vOutput .= vKey " " obj.item(vKey) "`r`n"
MsgBox, % vOutput

;ARRAY TYPE 4: CSD, case sensitive, date creation order
obj := ComObjCreate("Scripting.Dictionary")
Loop, Parse, vText, % ","
	if !(obj.item("" A_LoopField) = "")
		obj.item("" A_LoopField)++
	else
		obj.item("" A_LoopField) := 1
vOutput := ""
for vKey in obj
	vOutput .= vKey " " obj.item(vKey) "`r`n"
MsgBox, % vOutput
return
Btw @Helgef. Woah, that's a lot of 'Jee' even for me. I only use my handle for functions and occasionally for global variables. It's kind of confusing using it as example text, I removed them on my test to help follow the code. You even hardcoded the word 'Cheers' into the code, but that's a word that you use more than me isn't it?
Last edited by jeeswg on 07 May 2017, 07:47, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: objects: ordered array (function syntax to method syntax)

07 May 2017, 07:04

jeeswg wrote: However, it seems so awkward to create custom classes that resemble anything that I would want to use, that I may consider giving up on the idea of custom classes entirely. I'd be interested to know if anyone has done anything useful with custom classes (apart from generating integers), including retrieving the parent of an object.
Why is it awkward to use custom classes for something you would want to use? :crazy: I think there are plenty of useful custom classes scattered around the forum. What is the parent of an object? You can always define a parent key in your objects, if desired.
jeeswg wrote:My main goal in this topic is achieving:
- 4 types of array: CSA CSD CIA CID (case (in)sensitive, keys returned in alphabetical/creation date order)
Anyhow here are examples regarding 4 types of array, that don't use a custom class:
I'll take a look.
jeeswg wrote: Btw @Helgef. Woah, that's a lot of 'Jee' even for me...
It was just meant as a friendly tease. Indeed, the cheers was habitual. :wave:
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: objects: ordered array (function syntax to method syntax)

13 May 2017, 17:52

A quick recap, I'm trying to understand OrderedArray() (which essentially I believe I do now) and convert it from function syntax to method syntax.

I've added comments to OrderedArray(), which will hopefully make, it, and custom classes more generally, much easier to understand. I need to finish converting OrderedArray() from function syntax to method syntax, I'm having trouble with _NewEnum() and Next(). (I've tried many things not just the code below.)

I know that there may be alternatives to how OrderedArray() did _NewEnum()/Next(), but I'm trying to make the method syntax version as similar as possible to the original function syntax version. Thanks for reading.

Code: Select all

;script based on:
;[AHK_L] For Loop in order of key-value pair creation - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/61792-ahk-l-for-loop-in-order-of-key-value-pair-creation/

;another similar script:
;Ordered Array - Scripts and Functions - AutoHotkey Community
;https://autohotkey.com/board/topic/94043-ordered-array/

q::
if InStr(A_ThisHotkey, "q")
	obj := OrderedArray()

w::
if InStr(A_ThisHotkey, "w")
	obj := new OrderedArray2

obj.one := 1
obj.two := 2
obj.three := 3
obj.four := 4
t := ""
for k,v in obj
	t .= k "=" v "`n"
t .= "`n"

MsgBox % t
MsgBox, % obj.one
MsgBox, % obj.two
MsgBox, % obj._keys.1
MsgBox, % obj._keys.2

;based on code from:
;For-loop
;https://autohotkey.com/docs/commands/For.htm
_enum := obj._keys._NewEnum()
if IsObject(_enum)
	while _enum.Next(Key, Value)
		MsgBox, % Key " " Value
return

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

OrderedArray()
{
	;base := Object(key1, value1/obj1, key2, value2/obj2)
	;return Object(key1, value1/obj1, key2, value2/obj2)

	static base := Object("__Set", "oaSet", "_NewEnum", "oaNewEnum")
	return Object("_keys", Object(), "base", base)
}
oaSet(obj, k, v)
{
	;the __Set meta-function is only called when a key doesn't exist
	obj._keys.Push(k) ;equivalent to ObjRawSet(obj._keys, obj._keys.MaxIndex()+1, k)
	;since there is no 'return' line, AHK does the equivalent of ObjRawSet(obj, k, v)

	;equivalent code to the one-liner above:
	;if ObjHasKey(obj._keys, 1)
	;	ObjRawSet(obj._keys, obj._keys.MaxIndex()+1, k)
	;else
	;	ObjRawSet(obj._keys, 1, k)
	;ObjRawSet(obj, k, v)
	;return
}
oaNewEnum(obj)
{
	;base := Object(key1, value1/obj1)
	;return Object(key1, value1/obj1, key2, value2/obj2, key3, value3/obj3)

	static base := Object("Next", "oaEnumNext")
	return Object("obj", obj, "enum", obj._keys._NewEnum(), "base", base)
}
oaEnumNext(e, ByRef k, ByRef v="")
{
	;e is the enumerator object, which has keys: obj and enum
	;i/k (below) are key/value pairs from obj._keys
	;r is a return value, which is true if there is a next key, or false if there are no remaining keys

	if r := e.enum.Next(i,k)
		v := e.obj[k]
	return r
}

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

class OrderedArray2
{
	__New()
	{
		this._keys := {}
	}
	__Set(k, v)
	{
		this._keys.Push(k)
	}
	_NewEnum() ;not working
	{
		static base := Object("Next", this.Next)
		;return Object("obj", this, "enum", this._keys._NewEnum())
		return Object("obj", this, "enum", this._keys._NewEnum(), "base", base)
	}
	Next(e, ByRef k, ByRef v="") ;not working
	{
		if r := e.enum.Next(i,k)
			v := e.obj[k]
		return r
	}
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4663
Joined: 17 Jul 2016, 01:02
Contact:

Re: objects: ordered array (function syntax to method syntax)

14 May 2017, 04:19

So basically you just want to change from

Code: Select all

obj := OrderedArray()
to

Code: Select all

obj := new OrderedArray
and have one class with a few methods instead of no class and a few functions?
You can just put the functions in class {.} and set the base methods to the class methods instead of the functions, like this,

Code: Select all

class OrderedArray {
	__new(){		
		static base := Object("__Set", ObjBindMethod(OrderedArray,"oaSet"), "_NewEnum", ObjBindMethod(OrderedArray,"oaNewEnum"))
		return Object("_keys", Object(), "base", base)
	}
	oaSet(obj, k, v){
		obj._keys.Push(k)
	}
	oaNewEnum(obj){
		static base := Object("Next", ObjBindMethod(OrderedArray,"oaEnumNext"))
		return Object("obj", obj, "enum", obj._keys._NewEnum(), "base", base)
	}
	oaEnumNext(e, ByRef k, ByRef v=""){
		if r := e.enum.Next(i,k)
			v := e.obj[k]
		return r
	}
}
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: objects: ordered array (function syntax to method syntax)

14 May 2017, 06:02

I have some working code, OrderedArray2 works, OrderedArray3 doesn't work.

In converting, the main issue seems to be that _NewEnum() needs to create an enumerator object with a Next() method.

Perhaps ObjBindMethod as Helgef used, or a new class based on the custom class as HotKeyIt used, is the way to do this. However the original class didn't use 'ObjBindMethod' or 'new', so I'm wondering if there's another way.

Code: Select all

;script based on:
;[AHK_L] For Loop in order of key-value pair creation - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/61792-ahk-l-for-loop-in-order-of-key-value-pair-creation/

;similar script:
;Ordered Array - Scripts and Functions - AutoHotkey Community
;https://autohotkey.com/board/topic/94043-ordered-array/

q::
if InStr(A_ThisHotkey, "q")
	obj := OrderedArray()

w::
if InStr(A_ThisHotkey, "w")
	obj := new OrderedArray2

;e:: ;not working, crashes AHK
if InStr(A_ThisHotkey, "e")
	obj := new OrderedArray3

obj.one := 1
obj.two := 2
obj.three := 3
obj.four := 4
t := ""
for k,v in obj
	t .= k "=" v "`n"
t .= "`n"

MsgBox % t
MsgBox, % obj.one
MsgBox, % obj.two
MsgBox, % obj._keys.1
MsgBox, % obj._keys.2

;based on code from:
;For-loop
;https://autohotkey.com/docs/commands/For.htm
_enum := obj._keys._NewEnum()
if IsObject(_enum)
	while _enum.Next(Key, Value)
		MsgBox, % Key " " Value
return

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

OrderedArray()
{
	;base := Object(key1, value1/obj1, key2, value2/obj2)
	;return Object(key1, value1/obj1, key2, value2/obj2)
	static base := Object("__Set", "oaSet", "_NewEnum", "oaNewEnum")
	return Object("_keys", Object(), "base", base)
}
oaSet(obj, k, v)
{
	;the __Set meta-function is only called when a key doesn't exist
	obj._keys.Push(k) ;equivalent to ObjRawSet(obj._keys, obj._keys.MaxIndex()+1, k)
	;since there is no 'return' line, AHK does equivalent of ObjRawSet(obj, k, v)

	;equivalent code to the one-liner above:
	;if ObjHasKey(obj._keys, 1)
	;	ObjRawSet(obj._keys, obj._keys.MaxIndex()+1, k)
	;else
	;	ObjRawSet(obj._keys, 1, k)
	;ObjRawSet(obj, k, v)
	;return
}
oaNewEnum(obj)
{
	;base := Object(key1, value1/obj1)
	;return Object(key1, value1/obj1, key2, value2/obj2, key3, value3/obj3)
	static base := Object("Next", "oaEnumNext")
	return Object("obj", obj, "enum", obj._keys._NewEnum(), "base", base)
}
oaEnumNext(e, ByRef k, ByRef v="")
{
	;e is the enumerator object, which has keys obj and enum
	;i/k (below) are key/value pairs from obj._keys
	;r is a return value, which is true if there is a next key, or false if there are no remaining keys
	if r := e.enum.Next(i,k)
		v := e.obj[k]
	return r
}

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

class OrderedArray2
{
	__New()
	{
		this._keys := {}
	}
	__Set(k, v)
	{
		this._keys.Push(k)
	}
	_NewEnum()
	{
		static base := Object("Next", ObjBindMethod(OrderedArray2, "Next"))
		return Object("obj", this, "enum", this._keys._NewEnum(), "base", base)
	}
	Next(e, ByRef k, ByRef v="")
	{
		if r := e.enum.Next(i,k)
			v := e.obj[k]
		return r
	}
}

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

class OrderedArray3
{
	__New()
	{
		this._keys := {}
	}
	__Set(k, v)
	{
		this._keys.Push(k)
	}
	_NewEnum() ;not working
	{
		;static base := Object("Next", OrderedArray3.Next)
		;return Object("obj", this, "enum", (new OrderedArray3(this))._NewEnum(), "base", base)

		return Object("obj", this, "enum", (new OrderedArray3(this))._NewEnum())
	}
	Next(ByRef k, ByRef v="")
	{
		if r := enum.Next(i,k)
			v := obj[k]
		return r
	}
}

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

@Helgef
Many thanks, that's a really interesting approach to conversion between the 2 syntaxes.

@HotKeyIt
Many thanks, you seem to have anticipated all sorts of things I would want to add to the custom class, I think it even has multi-dimensional support.

I was having trouble getting the _NewEnum to work in my OrderedArray3 attempt above, based on this code from your example:

Code: Select all

    static base := Object("Next", OrderedArray._EnumNext)
    if (func="_NewEnum") ; Return an enumerator wrapping our _keys array's enumerator:
      return Object("obj", this, "enum", (new OrderedArray(this))._NewEnum(), "base", base)
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: objects: ordered array (function syntax to method syntax)

14 May 2017, 11:45

jeeswg wrote:@HotKeyIt
Many thanks, you seem to have anticipated all sorts of things I would want to add to the custom class, I think it even has multi-dimensional support.
Correct, as example shows it should handle all methods and also multi dimensional arrays.

OrderedArray3:

Code: Select all

o:=new OrderedArray3
o.b:=1,o.a:=2
for k,v in o
  MsgBox % k "`n" v
class OrderedArray3
{
	__New()
	{
		ObjRawSet(this,"_keys",{})
	}
	__Set(k, v)
	{
		this._keys.Push(k)
	}
	_NewEnum() ;not working
	{
		static base := Object("Next", OrderedArray3.Next)
		return Object("obj", this, "enum", this._keys._NewEnum(), "base", base)
	}
	Next(ByRef k, ByRef v="")
	{
		if r := this.enum.Next(i,k)
			v := this.obj[k]
		return r
	}
}
EDIT:
Fix this.obj[k]
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: objects: ordered array (function syntax to method syntax)

14 May 2017, 12:50

@HotKeyIt.
Btw I think v := obj[k] needs to be changed to v := this.obj[k] and then you can retrieve the values as well as the keys.

Thanks so much for this, with your version of OrderedArray3, and thanks to you and Helgef for other posts above, I've now cleared up pretty much all of my custom class issues. Hopefully all the core material is here for anyone who wants to pursue custom classes, and I'll try to incorporate the information here into my objects tutorial.

They are very dense these object custom classes, so many potential problems and things to understand in only a few lines of code. There is not too much documentation for _NewEnum(), and almost nothing for Next() which has caused a lot of the problems I've experienced.

I will now look at a variant script for OrderedArray and a case-sensitive array script, and see if I can convert those ...

Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: objects: ordered array (function syntax to method syntax)

14 May 2017, 17:05

jeeswg wrote:@HotKeyIt.
Btw I think v := obj[k] needs to be changed to v := this.obj[k] and then you can retrieve the values as well as the keys.
Correct, I have updated above.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: objects: ordered array (function syntax to method syntax)

16 May 2017, 18:47

Here is a working attempt at converting (a slight variant) of the CSobj class from function syntax to method syntax.

I might use it as a basis for a future CSobj class variant.

Code: Select all

;based on:
;Case sensitive variables possible? - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/61829-case-sensitive-variables-possible/
;How make Associative array keys case sensitive - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/61840-how-make-associative-array-keys-case-sensitive/

obj := new CSobj
obj["Q"] := 1
obj["w"] := 2
obj["E"] := 3
obj["q"] := 4
obj["W"] := 5
obj["e"] := 6

MsgBox, % obj.count()
MsgBox, % obj["Q"]
MsgBox, % obj["q"]

vOutput := ""
for vKey, vValue in obj
	vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
return

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

class CSobj
{
	__New()
	{
		ObjRawSet(this, "__sd_obj__", ComObjCreate("Scripting.Dictionary"))
	}
	__Get(key)
	{
		return, this.__sd_obj__.item("" key)
	}
	__Set(key, value)
	{
		this.__sd_obj__.item("" key) := value
		return, false
	}
	_NewEnum()
	{
		return, this
	}
	Next(ByRef key = "", ByRef val = "")
	{
		static Enum
		if not Enum
			Enum := this.__sd_obj__._NewEnum
		if Not Enum[key], val:=this[key]
			return, Enum:=false
		return, true
	}
	__Call(name)
	{
		if (name = "count")
			return, this.__sd_obj__.count
	}
}
==================================================

Links:

[case-insensitive array, date order]
[AHK_L] For Loop in order of key-value pair creation - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/6179 ... -creation/
[case insensitive array, date order][more functionality]
Ordered Array - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/94043-ordered-array/

[case-sensitive array, date order]
Case sensitive variables possible? - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/6182 ... -possible/
How make Associative array keys case sensitive - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/6184 ... sensitive/

[case-insensitive array, alphabetical order][AHK's standard arrays]
Object
https://autohotkey.com/docs/objects/Object.htm

==================================================

CASE-SENSITIVE ARRAY, ALPHABETICAL ORDER

Has anybody got any ideas for the best case sensitive alphabetical order array?

(A Scripting.Dictionary is object is case sensitive but stores keys in the order added.)

Use a Scripting.Dictionary object, enumerate the keys, and sort them at the enum stage?

Store them in an AHK case insensitive array as different objects?
E.g.
objects: get correct case key name - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 41#p147841

Or some other way? 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”

Who is online

Users browsing this forum: arczi_87, geoffh, inseption86, mikeyww, XMCQCX and 65 guests