[updated: 2019-11-12]
jeeswg's objects tutorial
==================================================
skip to second post:
jeeswg's objects tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=29232&p=147112#p147112
see also:
jeeswg's object classes tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=54588
==================================================
CONTENTS
> INTRO
> TERMINOLOGY
> OBJECT FUNCTIONS (OBJXXX FUNCTIONS)
> LINKS (DOCUMENTATION)
> FURTHER ARRAY/MAP FUNCTIONALITY (NOT CURRENTLY BUILT-IN)
> OPERATORS / KEYWORDS / SYNTAX
> FUNCTIONS THAT COMMONLY OUTPUT OBJECTS
> COM OBJECT FUNCTIONS (COMOBJXXX FUNCTIONS)
> GENERAL EXAMPLES
==================================================
> INTRO
- We will introduce a few concepts relating to arrays/objects.
- Then we will introduce the built-in object functions and operators etc.
- Then we will present some classic object examples.
- Lots of concepts are introduced, however, it might be best to skip straight to the GENERAL EXAMPLES section.
==================================================
> TERMINOLOGY
TYPES
- When learning a new programming language, I expect certain classic variable types to be available:
string: e.g. var := "abc", var := ""
integer: e.g. var := 123, var := 0
float: e.g. var := 123.123, var := 0.0
linear array: e.g. obj := ["value1", "value2", "value3"]
e.g. obj := Array("value1", "value2", "value3") (equivalent to line above)
associative array: e.g. obj := {key1:"value1", key2:"value2", key3:"value3"}
e.g. obj := Object("key1", "value1", "key2", "value2", "key3", "value3") (equivalent to line above)
LINEAR ARRAYS/ASSOCIATIVE ARRAYS
- This tutorial focuses on linear/associative arrays, which are both kinds of data structure.
- They can be used to store many things: e.g. data (e.g. strings/integers/floats), properties/methods (similar to functions), COM objects, other linear/associative arrays.
- In linear arrays, each item (value) has a number (index).
. E.g. from 0 to n (0-based indexes), or 1 to n (1-based indexes).
- AutoHotkey uses 1-based indexes.
- In associative arrays, each item (value) has a label (a key name). These are known as key-value pairs.
- E.g. in AutoHotkey, you can have integer key names e.g. '123', and string key names e.g. 'abc'.
- You cannot have more than one key with the same name.
- You have to be careful when assigning keys with names that look like an integer: e.g. you can have a key called '1' where 1 is an integer, and a key called '1' where 1 is a string. In AHK v1, doing obj[1] := value and obj["1"] := value, creates 2 separate keys.
- AHK v1 has a 'basic object' type, which has a dual purpose, it is used for both linear arrays and associative arrays.
- AHK v2 makes distinctions between types, e.g. arrays and maps.
- There are many data structures equivalent/similar to linear arrays e.g. vectors/tuples.
- Linear arrays are often just called 'arrays'.
Array data structure - Wikipedia
https://en.wikipedia.org/wiki/Array_data_structure
- There are many data structures equivalent/similar to associative arrays e.g. maps/dictionaries.Because the mathematical concept of a matrix can be represented as a two-dimensional grid, two-dimensional arrays are also sometimes called matrices. In some cases the term "vector" is used in computing to refer to an array, although tuples rather than vectors are the more mathematically correct equivalent. Tables are often implemented in the form of arrays, especially lookup tables; the word table is sometimes used as a synonym of array.
...
Arrays are used to implement other data structures, such as lists, heaps, hash tables, deques, queues, stacks, strings, and VLists.
Associative array - Wikipedia
https://en.wikipedia.org/wiki/Associative_array
- Links:In computer science, an associative array, map, symbol table, or dictionary is an abstract data type composed of a collection of (key, value) pairs, such that each possible key appears at most once in the collection.
Data structure - Wikipedia
https://en.wikipedia.org/wiki/Data_structure
List of data structures - Wikipedia
https://en.wikipedia.org/wiki/List_of_data_structures
Data type - Wikipedia
https://en.wikipedia.org/wiki/Data_type
REFERENCE COUNT
- When referring to objects, you can either copy the object, or create a reference to it. This is similar to copying a file, or creating a link to it (a .lnk file).
- Objects often keep a reference count. When the user creates a new reference, they should increment the reference count by 1. When the user deletes an existing reference, they should decrement the reference count by 1.
- When an object's reference count reaches 0, the object can be safely deleted.
DETAILS
- Linear/associative array types may differ within and between programming languages. E.g.:
- AHK: arrays start at 1 (not 0).
- AHK v1: key names are case insensitive (not case sensitive).
- AHK v1: key names are stored alphabetically (not by creation order).
- A COM object SafeArray starts at 0.
- A Scripting.Dictionary object for example, uses case-sensitive key names, and stores the creation order for keys.
- Other restrictions might be to restrict the key name or value to a certain type.
- Some array types allow you to use the same key name more than once.
CLASSES
- Whereas associative arrays are typically used to store an unlimited number of values (raw data), with key names that we do not know in advance; classes are typically used for a small, finite number of properties/methods which he have determined in advance.
- In AHK v1, the 'basic object' type, used for linear/associative arrays, is also used for classes. Either a class definition, e.g. 'class MyClass', is created, which specifies properties/methods; or a basic object is created, and properties/methods are placed into it.
- You have a blueprint class object, from which you can make one or more instance objects.
- A custom __Set method (meta-function) can be used to prevent key-value pairs being assigned to an instance object.
- Classes are discussed here:
jeeswg's object classes tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=54588
==================================================
> OBJECT FUNCTIONS (OBJXXX FUNCTIONS)
ARRAY FUNCTIONS
Array - create array [can also be done via obj := []]
ARRAY FUNCTIONS (WITH METHOD EQUIVALENTS)
ObjInsertAt/ObjRemoveAt - add/remove a value at a specific position
ObjLength - get the key name for the biggest integer key
ObjPush/ObjPop - add/remove a value at the end of the array
OBJECT/ARRAY/MAP FUNCTIONS (WITH METHOD EQUIVALENTS)
ObjClone - returns a 'shallow copy' of the object [to fully 'copy' an object's contents, you may need other approaches]
ObjCount - get an object's key count
ObjDelete - delete a key
ObjHasKey - check if an object has a key (with a particular name)
ObjNewEnum - 'Returns a new enumerator to enumerate this object's key-value pairs. This method is usually not called directly, but by the for-loop.' [equivalent to the '_NewEnum' method]
ObjToString (may be implemented) - (see String)
OBJECT/ARRAY/MAP FUNCTIONS (WITHOUT METHOD EQUIVALENTS)
Func - create a function reference [e.g. fn := Func("StrLen"), len := %fn%(var)] [similar to ObjBindMethod]
IsObject - check if a variable is an object
ObjAddRef/ObjRelease - increment/decrement the reference count (the reference count is used to determine when an object can be safety deleted)
ObjBindMethod - create a function reference to a method [similar to Func.Bind]
Object (AHK v1) (if 1 parameter which is an object) - object to address and increase reference count
Object (AHK v1/v2) (if 1 parameter which is an number) - address to object
Object (AHK v1/v2) (for an even number of parameters) - create map [can also be done via obj := {}]
ObjGetBase/ObjSetBase - get/set an object's base object
String (AHK v2) - return a string based on the object's ToString method
Type (AHK v2) - return the variable's type e.g. String/Integer/Float
DEPRECATED (AHK V1)
ObjInsert - alternatives: InsertAt, Push, ObjRawSet
ObjRemove - alternatives: RemoveAt, Delete, Pop
DEPRECATED (AHK V2)
ObjGetAddress - get address of field's string buffer
ObjGetCapacity/ObjSetCapacity - get/set object's capacity (cf. obj.Length()/obj.Count()) or field's string capacity (cf. VarSetCapacity)
ObjMinIndex/ObjMaxIndex - get smallest/largest key name (for keys of integer type)
ObjRawGet/ObjRawSet - get/set a key's value (bypass any meta-functions)
INFO ON DEPRECATED FUNCTIONS IN AHK V2
Old OSs support | Deprecated functions | FileSelect | FileSave | DirSelect - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=64490
RELATED CONTROL FLOW STATEMENTSfunctions marked to be removed in the documentation (2.0-a103-56441b52):
ObjSetCapacity, ObjGetCapacity, ObjGetAddress, ObjMinIndex/MaxIndex, variable's string buffer concept, ObjRawGet, ObjRawSet.
And these could change: VarSetCapacity, GuiCtrl.Pos.X.
try, catch, finally
for
throw [and function Exception]
LINKS
list of every command/function/variable from across all versions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=74&t=27321&p=131642#p131642
list of every object type/property/method - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=44081&p=199847#p199847
==================================================
> LINKS (DOCUMENTATION)
There is a lot of material on objects in the documentation, in several different places.
This list makes it easier to ensure you've read everything, and gives you an indication of how long each page is.
INDIVIDUAL FUNCTIONS / CONTROL FLOW STATEMENTS
A list of key object-related links:
Size of htm in KB on the left (based on decompiled chm file v1.1.30.03).
[contain 'obj' in file name]
[or appear in 'objects' folder]
[or pages with similar titles/related pages]
6 https://autohotkey.com/docs/misc/Arrays.htm
[general (also methods): ObjClone, ObjCount, ObjDelete, ObjHasKey, ObjNewEnum]
[array (also methods): ObjInsertAt/ObjRemoveAt, ObjLength, ObjPush/ObjPop]
[deprecated (also methods): ObjInsert/ObjRemove]
[deprecated (also methods): ObjGetAddress, ObjGetCapacity/ObjSetCapacity, ObjMinIndex/ObjMaxIndex]
[general: ObjGetBase/ObjSetBase]
[deprecated: ObjRawGet/ObjRawSet]
18 https://autohotkey.com/docs/objects/Object.htm
[general: Array/Object]
49 https://autohotkey.com/docs/Objects.htm
12 https://autohotkey.com/docs/objects/File.htm
8 https://autohotkey.com/docs/commands/FileOpen.htm
1 https://autohotkey.com/docs/commands/Func.htm
6 https://autohotkey.com/docs/objects/Func.htm
6 https://autohotkey.com/docs/objects/Functor.htm
2 https://autohotkey.com/docs/objects/Enumerator.htm
[ComObjActive, ComObject]
[ComObjEnwrap/ComObjUnwrap, ComObjMissing, ComObjParameter]
8 https://autohotkey.com/docs/commands/ComObjActive.htm
4 https://autohotkey.com/docs/commands/ComObjArray.htm
5 https://autohotkey.com/docs/commands/ComObjConnect.htm
2 https://autohotkey.com/docs/commands/ComObjCreate.htm
2 https://autohotkey.com/docs/commands/ComObjError.htm
2 https://autohotkey.com/docs/commands/ComObjFlags.htm
2 https://autohotkey.com/docs/commands/ComObjGet.htm
4 https://autohotkey.com/docs/commands/ComObjQuery.htm
6 https://autohotkey.com/docs/commands/ComObjType.htm
1 https://autohotkey.com/docs/commands/ComObjValue.htm
1 https://autohotkey.com/docs/commands/IsObject.htm
[ObjAddRef/ObjRelease]
2 https://autohotkey.com/docs/commands/ObjAddRef.htm
1 https://autohotkey.com/docs/commands/ObjBindMethod.htm
[for, try/catch/finally, throw]
6 https://autohotkey.com/docs/commands/For.htm
4 https://autohotkey.com/docs/commands/Try.htm
2 https://autohotkey.com/docs/commands/Catch.htm
3 https://autohotkey.com/docs/commands/Finally.htm
3 https://autohotkey.com/docs/commands/Throw.htm
FURTHER
Alphabetical Command and Function Index | AutoHotkey
https://autohotkey.com/docs/commands/index.htm
AutoHotkey_L New Features | AutoHotkey
https://autohotkey.com/docs/AHKL_Features.htm
[function versions of methods]
Basic Object - Methods & Properties | AutoHotkey
https://autohotkey.com/docs/objects/Object.htm
[the [] and {} syntax]Each method also has an equivalent function, which can be used to bypass any custom behaviour implemented by the object
Objects - Definition & Usage | AutoHotkey
https://www.autohotkey.com/docs/Objects.htm#Usage
Objects - Definition & Usage | AutoHotkey
https://www.autohotkey.com/docs/Objects.htm#Usage_Associative_Arrays
Concepts and Conventions | AutoHotkey
https://autohotkey.com/docs/Concepts.htm#objects
https://autohotkey.com/docs/Concepts.htm#object-protocol
https://autohotkey.com/docs/Concepts.htm#caching
https://autohotkey.com/docs/Concepts.htm#methods
https://autohotkey.com/docs/Concepts.htm#references-to-objects
Scripting Language | AutoHotkey
https://autohotkey.com/docs/Language.htm#operators-for-objects
==================================================
> FURTHER ARRAY/MAP FUNCTIONALITY (NOT CURRENTLY BUILT-IN)
array - sort/reverse/shuffle
array/map - frequency count (case sensitive/insensitive)
array/map - get/set value/property/method
array/map - search/filter: has value
array/map - search/filter: list matching key names/values (or copy values) (e.g. key name/value contains/equals/starts/ends/RegExMatch)
array/map - search/filter: min/max key name/value
array/map - search/filter: remove duplicates (case sensitive/insensitive)
array/map - set default value
array/map - set multiple values
array/map - to string: join
array/map - to string: print object/key names/values
map - key names: case sensitive/case insensitive (will be in AHK v2)
map - key names: get correct case
variable - is array/map
==================================================
> OPERATORS / KEYWORDS / SYNTAX
OPERATORS IN RELATION TO OBJECTS
new - create an instance object based on a class instance e.g. obj := new MyClass
:= - assign: use obj := "", to delete an object reference, the variable then contains a blank string
= == - obj1 = obj2 and obj1 == obj2 are true if both object variables are references to the same object
<> != !== - similar logic to = and ==, but inverted
?: (ternary operator) && || and not or ! - an object reference is always considered true
contains in - not implemented
is - AHK v2 has if obj is "object" and if obj is class (where class is a class object)
note: when concatenating variables, an object variable acts as a blank string
KEYWORDS
base - can be used to set the base for an instance object, and it 'can be used to access the super-class versions of methods or properties which are overridden in a derived class'
class - define a class definition
this - refer to an instance object when defining a method (inside a class definition)
FURTHER SYNTAX
obj := [] [AHK v1: create AHK basic object]
obj := {} [AHK v1: create AHK basic object]
KEYS/PROPERTIES/METHODS (AHK V1)
obj["MyKeyOrProperty"]
obj.MyKeyOrProperty
obj.MyMethod()
obj[vKeyOrProperty]
obj[vKeyOrProperty]
obj[vMethod]()
KEYS/PROPERTIES/METHODS (AHK V2):
obj["MyKey"] [removed: obj.MyKey][see workarounds below]
obj.MyProperty
obj.MyMethod()
obj[vKey]
obj.%vProperty% [removed: obj[vProperty]]
obj.%vMethod%() [removed: obj[vMethod]()]
obj.MyKey workarounds:
{a: b, c: d} created keys in AHK v1, it now creates properties, so you can still use the obj.xxx syntax
you can still use the obj.xxx syntax for arrays (where 'xxx' is a positive integer) e.g. array.1 and array[1]
==================================================
> FUNCTIONS THAT COMMONLY OUTPUT OBJECTS
Note: this list may be incomplete.
AHK v1/v2:
Array/Object
ComObjActive/ComObjArray/ComObjCreate/ComObject/ComObjGet
Exception
FileOpen
Func
ObjBindMethod/ObjClone/ObjGetBase/ObjNewEnum
RegExMatch
StrSplit
AHK v2:
BufferAlloc
ClipboardAll
GuiCreate/MenuBarCreate/MenuCreate
SysGetIPAddresses
WinGetControls/WinGetControlsHwnd/WinGetList
AHK v2 built-in variables:
A_Args
==================================================
> COM OBJECT FUNCTIONS (COMOBJXXX FUNCTIONS)
ComObjActive - get a reference to a running COM object (specify its CLSID/Prog ID)
ComObjArray - create a SafeArray
ComObjConnect - specify event-handler functions for a COM object (function names of the form: 'PrefixEventName')
ComObjCreate - create a COM object (specify its CLSID/Prog ID/IID)
ComObject - wrap a value/SafeArray/COM object, wrap/unwrap a raw IDispatch pointer
ComObjError - enable/disable error notifications
ComObjFlags - get/set flags (e.g. F_OWNVALUE)
ComObjGet - get a reference to a running COM object (specify its display name)
ComObjQuery - get an interface pointer from a COM object (specify an SID/IID)
ComObjType - get type info (variant type/IName/IID/CName/CLSID) (not Prog ID)
ComObjValue - get a value/pointer stored in a COM wrapper object
Note: there is no official function called 'ComObj'
DEPRECATED
ComObjEnwrap - use ComObject() instead, and ObjAddRef() if needed
ComObjMissing - write two consecutive commas instead
ComObjParameter - use ComObject()
ComObjUnwrap - use ComObjValue() instead, and ObjAddRef() if needed
Link:
v2-changes
https://autohotkey.com/v2/v2-changes.htm
COMOBJMISSING
Recreate ComObjMissing in AHK v1/v2:
Code: Select all
m := ComObjMissing() ;AHK v1
m := ComObject(0xA, 0x80020004) ;AHK v1/v2
conversion logic, v1 = -> v1 := -> v2, two-way compatibility - Page 3 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=37&t=27069&p=131336#p131336
CUSTOM FUNCTIONS IN THE DOCUMENTATION
[ComVar/ComVarGet/ComVarSet/ComVarDel custom functions]
ComObjActive() - Syntax & Usage | AutoHotkey
https://www.autohotkey.com/docs/commands/ComObjActive.htm
ComObject - Syntax & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/commands/ComObject.htm
==================================================
> GENERAL EXAMPLES
Code: Select all
;[updated: 2019-11-12]
;==================================================
;BASICS (CREATE/DELETE OBJECTS, GET/SET A KEY'S VALUE)
;INI TO ARRAY
;INI TO ARRAY (HANDY TRICK)
;OBJECT() V. ARRAY() (AHK V1)
;KEY NAMES (NUMERIC V. STRING)
;FUNCTIONALITY OF OBJECT()
;REFER TO KEYS
;LOOPING THROUGH AN OBJECT'S KEYS AND VALUES
;FOR LOOP EXAMPLES
;A KEY CAN HAVE A VALUE OR CHILD KEYS, BUT NOT BOTH
;ARRAY STARTING AT 0/1/OTHER (AHK V1)
;ARRAY STARTING AT 0 (AHK V1/V2)
;PUSH KEYS / ASSIGN MULTIPLE KEYS
;KEY NAMES THAT CLASH WITH METHOD NAMES
;FREQUENCY COUNT (CASE INSENSITIVE) (SORT)
;FREQUENCY COUNT (CASE INSENSITIVE) (MAINTAIN ORDER)
;FREQUENCY COUNT (CASE SENSITIVE) (MAINTAIN ORDER/SORT)
;REMOVE DUPLICATES (CASE INSENSITIVE) (MAINTAIN ORDER/SORT)
;REMOVE DUPLICATES (CASE SENSITIVE) (MAINTAIN ORDER/SORT)
;==================================================
;BASICS (CREATE/DELETE OBJECTS, GET/SET A KEY'S VALUE)
;note 'array' is used to mean both linear array
;and associative array in this tutorial
;create an array/object (all 5 lines are equivalent)
obj := []
obj := Array()
obj := {}
obj := Object()
obj := StrSplit("")
;note in AHK v2:
;[]/Array/StrSplit will create an array
;{}/Object (and/or a replacement for Object) will create an object(/map?)
;create an array/object (all 5 lines are equivalent)
obj := ["value1", "value2", "value3"]
obj := Array("value1", "value2", "value3")
obj := {1:"value1", 2:"value2", 3:"value3"}
obj := Object(1, "value1", 2, "value2", 3, "value3")
obj := StrSplit("value1,value2,value3", ",")
;4 further examples equivalent to the 5 above:
objtemp := StrSplit("1,value1,2,value2,3,value3", ",")
obj := Object(objtemp*)
objtemp := StrSplit("1=value1,2=value2,3=value3", ["=", ","])
obj := Object(objtemp*)
obj := Object(StrSplit("1,value1,2,value2,3,value3", ",")*)
obj := Object(StrSplit("1=value1,2=value2,3=value3", ["=", ","])*)
;refer to a key
MsgBox, % obj.1
MsgBox, % obj[1] ;same as above
Loop 3
MsgBox, % obj[A_Index]
;delete an object
obj := ""
;list keys and values
vOutput := ""
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;==================================================
;INI TO ARRAY
;ini to array:
vText := "
(
a=1
b=2
c=3
)"
oArray := {}
Loop Parse, vText, % "`n", % "`r"
{
oTemp := StrSplit(A_LoopField, "=")
oArray[oTemp.1] := oTemp.2
}
MsgBox, % oArray.c
MsgBox, % oArray["c"]
vList := "a,b,c"
vOutput := ""
Loop Parse, vList, % ","
vOutput .= A_LoopField " " oArray[A_LoopField] "`r`n"
MsgBox, % vOutput
vOutput := ""
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;==================================================
;INI TO ARRAY (HANDY TRICK)
;ini to array:
vText := "
(
a=1
b=2
c=3
)"
oArray := Object(StrSplit(vText, ["=", "`n"])*)
vOutput := ""
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;==============================
;two columns to array:
vText := "
(
a 1
b 2
c 3
)"
oArray := Object(StrSplit(vText, ["`t", "`n"])*)
vOutput := ""
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;==============================
;note the * is for use with variadic functions:
;the following 4 lines are equivalent:
oArray := Object("a", "A", "b", "B", "c", "C")
oArray := Object(["a", "A", "b", "B", "c", "C"]*)
oTemp := ["a", "A", "b", "B", "c", "C"], oArray := Object(oTemp*)
oTemp := StrSplit("a,A,b,B,c,C", ","), oArray := Object(oTemp*)
;==================================================
;OBJECT() V. ARRAY() (AHK V1)
;Differentiating Between Array and Object - Ask for Help - AutoHotkey Community
;https://autohotkey.com/board/topic/95303-differentiating-between-array-and-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}.
;==================================================
;KEY NAMES (NUMERIC V. STRING)
;note: currently in AHK v2 (this may change):
;obj["1"] is equivalent to obj[1]
;and you cannot create a string key called '1'
;the rest of the information in this section is mostly AHK v1-specific
;get key's value - where key name looks numeric
obj := {}
obj[1] := "value1" ;key where '1' is numeric
obj["1"] := "value2" ;key where '1' is string
MsgBox, % obj[1] " " obj["1"] ;value1 value2
MsgBox, % obj.1 ;value1
obj := {}
obj["1"] := "value2"
MsgBox, % obj.1 ;(blank)
;get key's value - where key name doesn't look numeric
obj := {}
var := "mykey"
obj[var] := "value1"
MsgBox, % obj[var] ;value1
MsgBox, % obj.mykey ;value1
MsgBox, % obj["mykey"] ;value1
obj := {}
obj["a"] := "value1"
MsgBox, % obj.a ;value1
MsgBox, % obj["a"] ;value1
;define objects - where key name looks numeric
obj := {1:"value1", 2:"value2", 3:"value3"}
MsgBox, % obj.1 ;value1
obj := {"1":"value1", "2":"value2", "3":"value3"}
MsgBox, % obj.1 ;(blank)
obj := Object(1, "value1", 2, "value2", 3, "value3")
MsgBox, % obj.1 ;value1
obj := Object("1", "value1", "2", "value2", "3", "value3")
MsgBox, % obj.1 ;(blank)
obj := {0xF:"value15"}
MsgBox, % obj.0xF ;value15
MsgBox, % obj.0xf ;value15
MsgBox, % obj.15 ;value15
obj := {15:"value15"}
MsgBox, % obj.0xF ;value15
MsgBox, % obj.0xf ;value15
MsgBox, % obj.15 ;value15
;define objects - where key name doesn't look numeric
obj := {a:"value1", b:"value2", c:"value3"}
MsgBox, % obj.a ;value1
obj := {"a":"value1", "b":"value2", "c":"value3"}
MsgBox, % obj.a ;value1
var := "mykey"
obj := Object(var, "value1")
MsgBox, % obj.mykey ;value1
obj := Object("a", "value1", "b", "value2", "c", "value3")
MsgBox, % obj.a ;value1
;==================================================
;FUNCTIONALITY OF OBJECT()
;0 parameters: create an empty array
obj := Object()
;2n parameters (where n is a positive integer): create an array with keys
obj := Object("key1", "value1", "key2", "value2", "key3", "value3")
;1 parameter: get a pointer to the object (AHK v1 only)
address := Object(obj) ;AHK v1
ObjAddRef(address := &obj) ;AHK v1/v2
;1 parameter: get the object in a usable form (may be removed in AHK v2)
obj := Object(address)
;==================================================
;REFER TO KEYS
obj1 := {}
obj2 := {}
obj1.a := "A" ;this works
obj2["a"] := "A" ;this works
MsgBox, % obj1.a ;A
MsgBox, % obj2.a ;A
obj1.b.c := "BC" ;this does not work
obj2["b", "c"] := "BC" ;this works
MsgBox, % obj1.b.c ;(blank)
MsgBox, % obj2.b.c ;BC
obj1 := obj2 := ""
obj := {}
obj["a", "b", "c", "d", "e"] := "ABCDE"
MsgBox, % obj.a.b.c.d.e ;ABCDE
MsgBox, % obj.a.b["c"].d.e ;ABCDE
MsgBox, % obj["a"].b["c"].d["e"] ;ABCDE
MsgBox, % obj["a", "b"].c["d", "e"] ;ABCDE
var := "c"
MsgBox, % obj.a.b[var].d.e ;ABCDE
obj := {}
obj["a", "b", "c", "d", "e"] := "ABCDE"
MsgBox, % obj.a.b.c.d.e ;ABCDE
MsgBox, % obj.a.b["c"].d.e ;ABCDE
MsgBox, % obj["a", "b"].c["d", "e"] ;ABCDE
MsgBox, % obj["a"].b["c"].d["e"] ;ABCDE
MsgBox, % obj.a["b"].c["d"].e ;ABCDE
obj := {}
obj["a", "b", "c", "d", "e"] := "ABCDE"
obj2 := ["a", "b", "c", "d", "e"]
obj3 := ["a", "b", "c", "d"]
obj4 := ["b", "c", "d", "e"]
obj5 := ["b", "c", "d"]
MsgBox, % obj[obj2*] ;ABCDE
MsgBox, % obj[obj3*, "e"] ;(blank)
MsgBox, % obj[obj3*]["e"] ;ABCDE
MsgBox, % obj["a", obj4*] ;ABCDE
MsgBox, % obj["a", obj5*, "e"] ;(blank)
MsgBox, % obj["a", obj5*]["e"] ;ABCDE
;Functions
;https://autohotkey.com/docs/Functions.htm#VariadicCall
;Only the right-most parameter can be expanded this way. For example, Func(x, y*) is supported but Func(x*, y) is not.
;==================================================
;LOOPING THROUGH AN OBJECT'S KEYS AND VALUES
oArray := {a:"A", b:"B", c:"C"}
;generally this is how I would loop through an object:
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
;if values are not needed:
for vKey in oArray
vOutput .= vKey "`r`n"
;if keys are not needed (where '_' is a variable):
for _, vValue in oArray
vOutput .= vValue "`r`n"
;I would not do this (where 'each' is a variable):
for each, vValue in oArray
vOutput .= vValue "`r`n"
MsgBox, % vOutput
;if the object has numeric keys only, you could use the word
;'index' instead of 'key', but I'd stick with 'key' always
vOutput := ""
oArray := ["A", "B", "C"]
;oArray := {1:"A", 2:"B", 3:"C"} ;equivalent
for vIndex, vValue in oArray
vOutput .= vIndex " " vValue "`r`n"
MsgBox, % vOutput
oArray := ""
;==================================================
;FOR LOOP EXAMPLES
;for loop examples (loop through an array's key-value pairs)
;list keys and values: linear array
oArray := ["a", "b", "c"]
vOutput := ""
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;list keys: linear array
oArray := ["a", "b", "c"]
vOutput := ""
for vKey in oArray
vOutput .= vKey "`r`n"
MsgBox, % vOutput
;list keys and values: associative array
oArray := {a:"A", b:"B", c:"C"}
vOutput := ""
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;list keys and values: linear array (via StrSplit)
oArray := StrSplit("abc,def,ghi", ",")
vOutput := ""
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;list keys and values: linear array (directly)
vOutput := ""
for vKey, vValue in ["a", "b", "c"]
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;list keys and values: associative array (directly)
vOutput := ""
for vKey, vValue in {a:"A", b:"B", c:"C"}
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;list keys and values: linear array (via StrSplit) (directly)
vOutput := ""
for vKey, vValue in StrSplit("abc,def,ghi", ",")
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;==================================================
;A KEY CAN HAVE A VALUE OR CHILD KEYS, BUT NOT BOTH
;an object's key can have a value or it can have child keys but not both
obj := {}
obj["a"] := "A"
MsgBox, % obj.a
obj["a", "b"] := "AB"
MsgBox, % obj.a.b ;(blank) ;obj.a is a value so cannot have subkeys
obj["a"] := {} ;if we make obj.a an object, it can now be given child keys
obj["a", "b"] := "AB"
MsgBox, % obj.a.b ;AB
obj := {}
obj["a", "b", "c"] := "ABC"
MsgBox, % obj.a.b.c ;ABC
obj["a", "b", "c", "d"] := "ABCD"
MsgBox, % obj.a.b.c.d ;(blank)
obj.a.b.Delete("c")
obj["a", "b", "c", "d"] := "ABCD"
MsgBox, % obj.a.b.c.d ;ABCD
;==================================================
;ARRAY STARTING AT 0/1/OTHER (AHK V1)
;shift a linear array to start at a specific index
;array starting at 1
oArray := ["A", "B", "C"]
;array starting at 0
oArray := []
oArray.InsertAt(0, "A", "B", "C")
;array starting at 0 (one-liner)
(oArray := []).InsertAt(0, "A", "B", "C")
;array starting at 0 (alternative) (one-liner)
(oArray := ["A", "B", "C"]).RemoveAt(0)
;array starting at 11 (one-liner)
(oArray := []).InsertAt(11, "K", "L", "M")
;==================================================
;ARRAY STARTING AT 0 (AHK V1/V2)
oArray := ComObjArray(VT_VARIANT:=12, 3)
for vKey, vValue in StrSplit("zero,one,two", ",")
oArray[A_Index-1] := vValue
MsgBox, % oArray[0]
MsgBox, % oArray[1]
MsgBox, % oArray[2]
;==================================================
;PUSH KEYS / ASSIGN MULTIPLE KEYS
;APPEND KEYS TO A LINEAR ARRAY
;this will create keys 1, 2, 3
oArray := ["a", "b", "c"]
;we can add key 4 like so:
oArray.Push("d")
;or instead, we can add keys 4, 5, 6 like so:
oArray.Push("d", "e", "f")
;alternatively:
;we can assign key 4 like so:
oArray.4 := "d"
;or we can add(/overwrite) keys 4, 5, 6 like so:
for vKey, vValue in {4:"d", 5:"e", 6:"f"}
oArray[vKey] := vValue
;to list the keys
vOutput := ""
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;ADD(/OVERWRITE) KEYS TO A ASSOCIATIVE ARRAY
;this will create keys a, b, c
oArray := {a:"A", b:"B", c:"C"}
;we can add(/overwrite) keys d, e, f like so:
for vKey, vValue in {d:"D", e:"E", f:"F"}
oArray[vKey] := vValue
;to list the keys
vOutput := ""
for vKey, vValue in oArray
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
;==================================================
;KEY NAMES THAT CLASH WITH METHOD NAMES
;if you create a key with the same name as a method
;the method stops working until you delete the key
;note: this is a problem in AHK v1, but not in AHK v2
oArray := ["a", "b", "c"]
MsgBox, % oArray.Length() ;3
oArray["Length"] := "x"
MsgBox, % oArray.Length() ;(blank)
oArray.Delete("Length")
MsgBox, % oArray.Length() ;3
oArray := ["a", "b", "c"]
MsgBox, % oArray.HasKey(2) ;1
oArray["HasKey"] := "x"
MsgBox, % oArray.HasKey(2) ;(blank)
oArray.Delete("HasKey")
MsgBox, % oArray.HasKey(2) ;1
;for this reason, in the examples below,
;when we create keys with arbitrary names,
;we add an arbitrary prefix to prevent clashes,
;'z' was chosen since no AHK method names begin with 'z'
;==================================================
;FREQUENCY COUNT (CASE INSENSITIVE) (SORT)
;note: 'z' is used to avoid creating a key
;with the same name as a method
;q:: ;frequency count (case insensitive)
vText := "f,e,d,C,B,A,a,b,c,A,B,C"
oArray := {}
StrReplace(vText, ",",, vCount)
oArray.SetCapacity(vCount+1)
Loop Parse, vText, % ","
{
if oArray.HasKey("z" A_LoopField)
oArray["z" A_LoopField]++
else
oArray["z" A_LoopField] := 1
}
;list all items
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2*2)
for vKey, vValue in oArray
vOutput .= vValue "`t" SubStr(vKey, 2) "`r`n"
MsgBox, % vOutput
;list items that appear n times
vNum := 1
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2*2)
for vKey, vValue in oArray
{
if (vValue = vNum)
vOutput .= vValue "`t" SubStr(vKey, 2) "`r`n"
}
MsgBox, % vOutput
oArray := ""
return
;==================================================
;FREQUENCY COUNT (CASE INSENSITIVE) (MAINTAIN ORDER)
;q:: ;frequency count (case insensitive)
vText := "f,e,d,C,B,A,a,b,c,A,B,C"
oTemp := ComObjCreate("Scripting.Dictionary")
Loop Parse, vText, % ","
{
vTemp := Format("{:L}", A_LoopField)
if oTemp.Exists("" vTemp)
oTemp.Item["" vTemp]++
else
oTemp.Item["" vTemp] := 1
}
;list all items
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2*2)
oArray := {}
oArray.SetCapacity(oTemp.Count)
Loop Parse, vText, % ","
{
if !oArray.HasKey("z" A_LoopField)
{
oArray["z" A_LoopField] := 1
vOutput .= A_LoopField "`t" oTemp.Item["" Format("{:L}", A_LoopField)] "`r`n"
}
}
MsgBox, % vOutput
;list items that appear n times
vNum := 1
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2*2)
oArray := {}
oArray.SetCapacity(oTemp.Count)
Loop Parse, vText, % ","
{
if !oArray.HasKey("z" A_LoopField)
{
oArray["z" A_LoopField] := 1
vValue := oTemp.Item["" Format("{:L}", A_LoopField)]
if (vValue = vNum)
vOutput .= A_LoopField "`t" vValue "`r`n"
}
}
MsgBox, % vOutput
oArray := oTemp := ""
return
;==================================================
;FREQUENCY COUNT (CASE SENSITIVE) (MAINTAIN ORDER/SORT)
;w:: ;frequency count (case sensitive)
vText := "f,e,d,C,B,A,a,b,c,A,B,C"
oArray := ComObjCreate("Scripting.Dictionary")
;Sort, vText, CS D, ;add this line to sort the list
Loop Parse, vText, % ","
{
if oArray.Exists("" A_LoopField)
oArray.Item["" A_LoopField]++
else
oArray.Item["" A_LoopField] := 1
}
;list all items
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2*2)
for vKey in oArray
vOutput .= oArray.Item[vKey] "`t" vKey "`r`n"
MsgBox, % vOutput
;list items that appear n times
vNum := 1
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2*2)
for vKey in oArray
{
if (oArray.Item[vKey] = vNum)
vOutput .= oArray.Item[vKey] "`t" vKey "`r`n"
}
MsgBox, % vOutput
oArray := ""
return
;==================================================
;REMOVE DUPLICATES (CASE INSENSITIVE) (MAINTAIN ORDER/SORT)
;note: 'z' is used to avoid creating a key
;with the same name as a method
;q:: ;remove duplicates (case insensitive)
vText := "f,e,d,C,B,A,a,b,c,A,B,C"
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2*2)
oArray := {}
StrReplace(vText, ",",, vCount)
oArray.SetCapacity(vCount+1)
;Sort, vText, D, ;add this line to sort the list
Loop Parse, vText, % ","
{
if !oArray.HasKey("z" A_LoopField)
oArray["z" A_LoopField] := 1, vOutput .= A_LoopField "`r`n"
}
MsgBox, % vOutput
oArray := ""
return
;==================================================
;REMOVE DUPLICATES (CASE SENSITIVE) (MAINTAIN ORDER/SORT)
;w:: ;remove duplicates (case sensitive)
vText := "f,e,d,C,B,A,a,b,c,A,B,C"
vOutput := ""
VarSetCapacity(vOutput, StrLen(vText)*2*2)
oArray := ComObjCreate("Scripting.Dictionary")
;Sort, vText, CS D, ;add this line to sort the list
Loop Parse, vText, % ","
{
if !oArray.Exists("" A_LoopField)
oArray.Item["" A_LoopField] := 1, vOutput .= A_LoopField "`r`n"
}
MsgBox, % vOutput
oArray := ""
return
;==================================================
;REFERENCE COUNT
;get ref count (get reference count) (undocumented) (not recommended)
oArray := ["a", "b", "c"]
oArray2 := oArray
vCountRef := NumGet(&oArray + A_PtrSize)
MsgBox, % vCountRef ;2
oArray3 := oArray
vCountRef := NumGet(&oArray + A_PtrSize)
MsgBox, % vCountRef ;3
oArray3 := ""
vCountRef := NumGet(&oArray + A_PtrSize)
MsgBox, % vCountRef ;2
;get ref count (get reference count) (preferred)
oArray := ["a", "b", "c"]
oArray2 := oArray
ObjAddRef(&oArray)
vCountRef := ObjRelease(&oArray)
MsgBox, % vCountRef ;2
oArray3 := oArray
ObjAddRef(&oArray)
vCountRef := ObjRelease(&oArray)
MsgBox, % vCountRef ;3
oArray3 := ""
ObjAddRef(&oArray)
vCountRef := ObjRelease(&oArray)
MsgBox, % vCountRef ;2
;==================================================
;KEY COUNT
;get key count (undocumented) (not recommended)
;For a normal AHK array to get its key count:
;oArray := ["a", "b", "c"]
;vCount := NumGet(&oArray + 4*A_PtrSize)
;MsgBox, % vCount
;from:
;ObjCount() or ObjLength() or ObjLen() - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=37&t=3950&p=21579#p21578
;get key count (preferred)
;the .Count() method and ObjCount() were added in AHK v1.1.29
oArray := ["a", "b", "c"]
MsgBox, % oArray.Count()
MsgBox, % ObjCount(oArray)
;get key count (also)
oArray := ["a", "b", "c"]
vCount := 0
for vKey, vValue in oArray
vCount := A_Index
MsgBox, % vCount
;==================================================
Spoiler
==================================================