Multi-level associative arrays

Get help with using AutoHotkey and its commands and hotkeys
TAC109
Posts: 732
Joined: 02 Oct 2013, 19:41
Location: New Zealand

Multi-level associative arrays

26 May 2014, 18:42

I'm trying to get to grips with multi-level associative arrays.

I find the documentation to be very brief and lacking useful examples.

How do I define an empty multi-level associative array?

How do I use the 'for' command to extract values from a multi-level associative array?

Can I enumerate the key:values in reverse order?

(I believe I understand how to insert key:value pairs into a multi-level associative array.)

Thanks for any help.
My scripts:-
XRef - Produces Cross Reference lists for scripts
ReClip - A Text Reformatting and Clip Management utility
User avatar
joedf
Posts: 8139
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: Multi-level associative arrays

26 May 2014, 21:20

All arrays in autohotkey are technically "associative arrays". Is your question about the for loop in reverse and/or an array declaration?
arr := { keyname1: var1, key2: valueB }
arr := { 1: blah, 2: asdf }
Is the order really important? Is it possible to store the information in reverse? Are you using numbers as Keys? If so, what have you tried?
TAC109
Posts: 732
Joined: 02 Oct 2013, 19:41
Location: New Zealand

Re: Multi-level associative arrays

26 May 2014, 22:26

Sorry if I'm no being clear. I understand associative arrays.

I've got 3 questions:-
1. How do I define an empty multi-level associative array?

2. How do I use the 'for' command to extract values from a multi-level associative array?

3. Can I enumerate the key:values in reverse order?
The first two relate to multi-level associative arrays and the last is more general.

Thanks
My scripts:-
XRef - Produces Cross Reference lists for scripts
ReClip - A Text Reformatting and Clip Management utility
User avatar
joedf
Posts: 8139
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: Multi-level associative arrays

26 May 2014, 22:50

1. Ok, since associative arrays must have keys, this would be the closest...
An example of an "empty" 3x3 array:

Code: Select all

MySubArray_1 := { 1:{ }, 2:{ }, 3:{ } }
MySubArray_2 := { 1:{ }, 2:{ }, 3:{ } }
MySubArray_3 := { 1:{ }, 2:{ }, 3:{ } }
MyArray := { 1:MySubArray_1, 2:MySubArray_2, 3:MySubArray_3 }
2. not sure, but here is an example:

Code: Select all

MySubArray_1 := { 1:"A", 2:"B", 3:"C" }
MySubArray_2 := { 1:"D", 2:"E", 3:"F" }
MySubArray_3 := { 1:"G", 2:"H", 3:"I" }
MyArray := { 1:MySubArray_1, 2:MySubArray_2, 3:MySubArray_3 }

for every, SubArray in MyArray
	for each, item in SubArray
		MsgBox SubArray: %every%`nValue [%each%]: %item%
3. Well, in reverse could be done without much trouble if the arrays have numbers as keys...
An example: I made a neat little recursive Array-reverse function there ;)

Code: Select all

MySubArray_1 := { 1:"A", 2:"B", 3:"C" }
MySubArray_2 := { 1:"D", 2:"E", 3:"F" }
MySubArray_3 := { 1:"G", 2:"H", 3:"I" }
MyArray := { 1:MySubArray_1, 2:MySubArray_2, 3:MySubArray_3 }

MyArray:=ReverseArray(MyArray)

for every, SubArray in MyArray
	for each, item in SubArray
		MsgBox SubArray: %every%`nValue [%each%]: %item%

ReverseArray(Arr) {
	newArr := Object()
	Loop % k:=Arr.MaxIndex()
		if IsObject(Arr[A_index])
			newArr[k-(A_index-1)]:=ReverseArray(Arr[A_index])
		else
			newArr[k-(A_index-1)]:=Arr[A_index]
	return newArr
}
Cheers, hope this helps :)
User avatar
LinearSpoon
Posts: 156
Joined: 29 Sep 2013, 22:55

Re: Multi-level associative arrays

27 May 2014, 02:19

2. More generally, you simply check if each item you come to is a subarray, and if so go through a for loop on the subarray. This is one of the few algorithms it feels natural to write recursively. This serialize function is something I wrote a while back to quickly print out the contents of most objects. It might give you some ideas.

Code: Select all

msgbox % Serialize( {} )  ;Empty array
msgbox % Serialize( [1,2,3] )  ;Simple array
msgbox % Serialize( {apples:"bananas", grapes:"oranges", pickles:[1,2,3]} )  ;Associative array with a subarray
msgbox % Serialize( new example )  ;Class instance

serialize(obj)
{
  if !IsObject(obj)
    return ""
  str := "{"
  for k,v in obj
  {
    if IsObject(v)
    {
      if IsFunc(v.name)
        v := "Function"
      else
        v := serialize(v)
    }
    str .= k ":" v ", "
  }
  if IsObject(obj.base)
    str .= "base:" serialize(obj.base) ", "
  return RegexReplace(str, ", $", "") "}"
}

class example
{
  __New()
  {
    this.a := "A"
    this.b := "B"
  }
}
3. You could write a custom enumerator. This allows you to continue using for-loop syntax, and preserves the original object.

Code: Select all

;Sample data
arr := { 1:"A", 2:"B", 3:"C", cats:"dogs", zebras:"lions"}

str := "Normal Iteration"
for k,v in arr
  str .= "`n" k " = " v
Msgbox %str%

str := "Reverse Iteration"
e := new reverseEnum(arr)
while e[k,v]
  str .= "`n" k " = " v
Msgbox %str%

;Enabling arr to use reverseEnum with a for loop
str := "Reverse For Loop"
arr._NewEnum := Func("getReverseEnum")
for k,v in arr
  str .= "`n" k " = " v
Msgbox %str%

getReverseEnum(obj)
{
  return new reverseEnum(obj)
}

class reverseEnum
{
  __New(obj)
  {
    this.items := {}     ;A list of keys and values as the obj currently contains
    this.count := 0      ;Count of items found in obj
    e := ObjNewEnum(obj) ;Get a normal enumerator
    while e[k,v]         ;Get all the keys and values and save them for later
      if (k != "_NewEnum")   ;Filter enum method if it comes up...
        this.items[++this.count] := {k:k,v:v}
    this.base.__Get := this.Next  ;Enables enum[k,v] to call enum.Next(k,v)
  }

  Next(byref k, byref v)
  {
    curItem := this.items[this.count]   ;Get the current item
    k := curItem.k, v := curItem.v      ;Set byref variables
    return (this.count-- >= 1)          ;Decrement count and return whether or not this is the last item
  }
}
As a side note, I find this to be misleading: for each, item in SubArray
It's nice that it reads as an English phrase, but someone not familiar with the syntax may wonder why there is a comma when it isn't appropriate grammatically, and it gives no indication that each is actually a variable name.
guest3456
Posts: 3240
Joined: 09 Oct 2013, 10:31

Re: Multi-level associative arrays

27 May 2014, 08:55

joedf wrote:1. Ok, since associative arrays must have keys, this would be the closest...
An example of an "empty" 3x3 array:

Code: Select all

MySubArray_1 := { 1:{ }, 2:{ }, 3:{ } }
MySubArray_2 := { 1:{ }, 2:{ }, 3:{ } }
MySubArray_3 := { 1:{ }, 2:{ }, 3:{ } }
MyArray := { 1:MySubArray_1, 2:MySubArray_2, 3:MySubArray_3 }
this is actually 3 levels deep and is a 3x3x1 array

User avatar
joedf
Posts: 8139
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: Multi-level associative arrays

27 May 2014, 08:57

Haha you're right :P
TAC109
Posts: 732
Joined: 02 Oct 2013, 19:41
Location: New Zealand

Re: Multi-level associative arrays

27 May 2014, 17:34

Thanks for the help everyone.

@joedf: thanks for your code that shows the basics.

@LinearSpoon: thanks for your example code, showing that the multi-level arrays can be unbalanced. As I want my object to reflect a folder with files and sub-folders, etc this will be very useful.
My scripts:-
XRef - Produces Cross Reference lists for scripts
ReClip - A Text Reformatting and Clip Management utility
User avatar
joedf
Posts: 8139
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: Multi-level associative arrays

27 May 2014, 17:39

No problemo! ;)

Return to “Ask For Help”

Who is online

Users browsing this forum: aifritz, blad4, jwwpua, tank, Xtra and 61 guests