Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

Print Array


  • Please log in to reply
8 replies to this topic
  • Guests
  • Last active:
  • Joined: --
Array2 := Object("test", "123")

Array := [{Name:"d", Location:"D:\tmp", Access:40, Date:20110821}
	, {Name:"c", Location:"D:\tmp\b", Access:999, Date:20110819}
	, {Name:"e", Location:"D:\somethingelse", Access:3, Date:20100115}
	, {Name:"a", Location:"E:\apps\data", Access:8, Date:20110730}
	, {Name:"b", Location:"C:\Windows\", Access:21, Date:20091230}
	, {Name:"f", ModifiedDate:20100910, Data:Array2}]
	
PrintArr(Array)

	
PrintArr(Arr, GuiNum=90, Option="x2 y0 w400 h500") {
	For index, obj in Arr {
		if ( A_Index = 1 ) {
			For key, value in obj {
				Columns .= key "|"	
				numColumn++
			}	
			Gui, %GuiNum%: Add, ListView, %Option%, %Columns%
		}
		RowNum := A_Index		
		Gui, %GuiNum%: default						;this is necessary
		LV_Add("")									;add a row
		For key, value in obj {
			LV_GetText(Header, 0, A_Index)			;check if the inserting value is in the correct column
			if (key <> Header) {	
				FoundHeader := False
				Loop % LV_GetCount("Column") {		;search the matching column
					LV_GetText(Header, 0, A_Index)
					if (key <> Header)
						continue
					else {
						FoundHeader := A_Index
						Break
					}
				}
				if !FoundHeader {
					LV_InsertCol(numColumn + 1, "", key)
					numColumn++
					ColNum := "Col" numColumn
				} else
					ColNum := "Col" FoundHeader
			} else
				ColNum := "Col" A_Index
			LV_Modify(RowNum, ColNum, (IsObject(value) ? "Object()" : value)) 	;insert the value
		}
	}
	Loop % LV_GetCount("Column") 		;the number of colums
		LV_ModifyCol(A_Index, "AutoHdr")		;adjust each column width
		
	Gui, %GuiNum%: Show,, Array
}
Feel free to modify it. Maybe object() can be displayed in a different color and if the user clicks on it, it creates another GUI or updates the current one to show the contents of the clicked object. I don't have enough time to develop it, so just throwing this idea to the community.

trueski
  • Members
  • 121 posts
  • Last active: Jun 25 2014 09:12 PM
  • Joined: 08 Apr 2008
I made a similar function that I think works a little cleaner. It will display up to ten levels deep. I could get it to allow more levels, but I don't have the need to right now, and it can get kind of confusing when trying to add more.

Thanks to Polythene for the anchor function.

Syntax:
PrintArray(Array, [DisplayOutput? = 0|1])

Array := Object()
Array[1] := "Test 1"
Array[2] := "Test 2"
Array[3] := Object()
Array[3][1] := "Test 3-1"
Array[3][2] := Object()
Array[3][2][1] := "Test 3-2-1"
Array[3][2][2] := "Test 3-2-2"
Array[3][3] := "Test 3-3"
Array[4] := "Test 4"

PrintArray(Array, 1)
ExitApp

PrintArray(Array, Display=0)
{
	Global PrintArray
	
	If (!IsObject(Array))
	Return ""
	
	Text := "Array" . "`r`n" . "("
	For Key1, Value1 in Array
	{
		If !(IsObject(Value1))
		     Text .= "`r`n`t[" . Key1 . "] => " . Value1
		Else Text .= "`r`n`t[" . Key1 . "] => " . "Array"
		
		If (IsObject(Value1))
		{
			Text .= "`r`n`t" . "("
			
			For Key2, Value2 in Value1
			{
				If !(IsObject(Value2))
		         Text .= "`r`n`t`t[" . Key2 . "] => " . Value2
		    Else Text .= "`r`n`t`t[" . Key2 . "] => " . "Array"
		
				If (IsObject(Value2))
		    {
					Text .= "`r`n`t`t" . "("
					
					For Key3, Value3 in Value2
			    {
						If !(IsObject(Value3))
		             Text .= "`r`n`t`t`t[" . Key3 . "] => " . Value3
		        Else Text .= "`r`n`t`t`t[" . Key3 . "] => " . "Array"
				
						If (IsObject(Value3))
		        {
							Text .= "`r`n`t`t`t" . "("
							
							For Key4, Value4 in Value3
			        {
								If !(IsObject(Value4))
		                 Text .= "`r`n`t`t`t`t[" . Key4 . "] => " . Value4
		            Else Text .= "`r`n`t`t`t`t[" . Key4 . "] => " . "Array"
								
								If (IsObject(Value4))
		            {
									Text .= "`r`n`t`t`t`t" . "("
									
									For Key5, Value5 in Value4
			            {
										If !(IsObject(Value5))
		                     Text .= "`r`n`t`t`t`t`t[" . Key5 . "] => " . Value5
		                Else Text .= "`r`n`t`t`t`t`t[" . Key5 . "] => " . "Array"
								
										If (IsObject(Value5))
		                {
											Text .= "`r`n`t`t`t`t`t" . "("
											
											For Key6, Value6 in Value5
			                {
												If !(IsObject(Value6))
		                         Text .= "`r`n`t`t`t`t`t`t[" . Key6 . "] => " . Value6
		                    Else Text .= "`r`n`t`t`t`t`t`t[" . Key6 . "] => " . "Array"
										
												If (IsObject(Value6))
		                    {
													Text .= "`r`n`t`t`t`t`t`t" . "("
													
													For Key7, Value7 in Value6
			                    {
														If !(IsObject(Value7))
		                             Text .= "`r`n`t`t`t`t`t`t`t[" . Key7 . "] => " . Value7
		                        Else Text .= "`r`n`t`t`t`t`t`t`t[" . Key7 . "] => " . "Array"
														
														If (IsObject(Value7))
		                        {
															Text .= "`r`n`t`t`t`t`t`t`t" . "("
															
															For Key8, Value8 in Value7
			                        {
																If !(IsObject(Value8))
		                                 Text .= "`r`n`t`t`t`t`t`t`t`t[" . Key8 . "] => " . Value8
		                            Else Text .= "`r`n`t`t`t`t`t`t`t`t[" . Key8 . "] => " . "Array"
																
																If (IsObject(Value8))
		                            {
																	Text .= "`r`n`t`t`t`t`t`t`t`t" . "("
																	
																	For Key9, Value9 in Value8
			                            {
																		If !(IsObject(Value9))
		                                     Text .= "`r`n`t`t`t`t`t`t`t`t`t[" . Key9 . "] => " . Value9
		                                Else Text .= "`r`n`t`t`t`t`t`t`t`t`t[" . Key9 . "] => " . "Array"
																		
																		If (IsObject(Value9))
		                                {
																			Text .= "`r`n`t`t`t`t`t`t`t`t`t" . "("
																			
																			For Key10, Value10 in Value9
			                                {
																				If !(IsObject(Value10))
		                                         Text .= "`r`n`t`t`t`t`t`t`t`t`t`t[" . Key10 . "] => " . Value10
		                                    Else Text .= "`r`n`t`t`t`t`t`t`t`t`t`t[" . Key10 . "] => " . "Array"
																			}
																		  
																			Text .= "`r`n`t`t`t`t`t`t`t`t`t" . ")"
																		}
																	}
																	
																	Text .= "`r`n`t`t`t`t`t`t`t`t" . ")"
																}
															}
															
															Text .= "`r`n`t`t`t`t`t`t`t" . ")"
														}
													}
													
													Text .= "`r`n`t`t`t`t`t`t" . ")"
												}
											}
										
										  Text .= "`r`n`t`t`t`t`t" . ")"
										}
									}
								
								  Text .= "`r`n`t`t`t`t" . ")"
								}
							}
						
						  Text .= "`r`n`t`t`t" . ")"
						}
					}
				
				  Text .= "`r`n`t`t" . ")"
				}
			}
		
		  Text .= "`r`n`t" . ")"
		}
	}

  Text .= "`r`n" . ")"
	
	If (!Display)
	Return Text
	
	Gui, PrintArray:+MaximizeBox
	Gui, PrintArray:Add, Edit, x12 y10 w450 h350 vPrintArray ReadOnly, %Text%
  Gui, PrintArray:Show, w476 h374, PrintArray
	Gui, PrintArray:+LastFound
	ControlSend, , {Right}
	WinWaitClose
  Return Text

	PrintArrayGuiSize:
  Anchor("PrintArray", "wh")
  Return

  PrintArrayGuiClose:
  Gui, PrintArray:Destroy
  Return
}

/*
	Function: Anchor
		Defines how controls should be automatically positioned relative to the new dimensions of a window when resized.

	Parameters:
		cl - a control HWND, associated variable name or ClassNN to operate on
		a - (optional) one or more of the anchors: 'x', 'y', 'w' (width) and 'h' (height),
			optionally followed by a relative factor, e.g. "x h0.5"
		r - (optional) true to redraw controls, recommended for GroupBox and Button types

	Examples:
> "xy" ; bounds a control to the bottom-left edge of the window
> "w0.5" ; any change in the width of the window will resize the width of the control on a 2:1 ratio
> "h" ; similar to above but directrly proportional to height

	Remarks:
		To assume the current window size for the new bounds of a control (i.e. resetting) simply omit the second and third parameters.
		However if the control had been created with DllCall() and has its own parent window,
			the container AutoHotkey created GUI must be made default with the +LastFound option prior to the call.
		For a complete example see anchor-example.ahk.

	License:
		- Version 4.60a <http://www.autohotkey.net/~Titan/#anchor>
		- Simplified BSD License <http://www.autohotkey.net/~Titan/license.txt>
*/
Anchor(i, a = "", r = false) {
	static c, cs = 12, cx = 255, cl = 0, g, gs = 8, gl = 0, gpi, gw, gh, z = 0, k = 0xffff
	If z = 0
		VarSetCapacity(g, gs * 99, 0), VarSetCapacity(c, cs * cx, 0), z := true
	If (!WinExist("ahk_id" . i)) {
		GuiControlGet, t, Hwnd, %i%
		If ErrorLevel = 0
			i := t
		Else ControlGet, i, Hwnd, , %i%
	}
	VarSetCapacity(gi, 68, 0), DllCall("GetWindowInfo", "UInt", gp := DllCall("GetParent", "UInt", i), "UInt", &gi)
		, giw := NumGet(gi, 28, "Int") - NumGet(gi, 20, "Int"), gih := NumGet(gi, 32, "Int") - NumGet(gi, 24, "Int")
	If (gp != gpi) {
		gpi := gp
		Loop, %gl%
			If (NumGet(g, cb := gs * (A_Index - 1)) == gp) {
				gw := NumGet(g, cb + 4, "Short"), gh := NumGet(g, cb + 6, "Short"), gf := 1
				Break
			}
		If (!gf)
			NumPut(gp, g, gl), NumPut(gw := giw, g, gl + 4, "Short"), NumPut(gh := gih, g, gl + 6, "Short"), gl += gs
	}
	ControlGetPos, dx, dy, dw, dh, , ahk_id %i%
	Loop, %cl%
		If (NumGet(c, cb := cs * (A_Index - 1)) == i) {
			If a =
			{
				cf = 1
				Break
			}
			giw -= gw, gih -= gh, as := 1, dx := NumGet(c, cb + 4, "Short"), dy := NumGet(c, cb + 6, "Short")
				, cw := dw, dw := NumGet(c, cb + 8, "Short"), ch := dh, dh := NumGet(c, cb + 10, "Short")
			Loop, Parse, a, xywh
				If A_Index > 1
					av := SubStr(a, as, 1), as += 1 + StrLen(A_LoopField)
						, d%av% += (InStr("yh", av) ? gih : giw) * (A_LoopField + 0 ? A_LoopField : 1)
			DllCall("SetWindowPos", "UInt", i, "Int", 0, "Int", dx, "Int", dy
				, "Int", InStr(a, "w") ? dw : cw, "Int", InStr(a, "h") ? dh : ch, "Int", 4)
			If r != 0
				DllCall("RedrawWindow", "UInt", i, "UInt", 0, "UInt", 0, "UInt", 0x0101) ; RDW_UPDATENOW | RDW_INVALIDATE
			Return
		}
	If cf != 1
		cb := cl, cl += cs
	bx := NumGet(gi, 48), by := NumGet(gi, 16, "Int") - NumGet(gi, 8, "Int") - gih - NumGet(gi, 52)
	If cf = 1
		dw -= giw - gw, dh -= gih - gh
	NumPut(i, c, cb), NumPut(dx - bx, c, cb + 4, "Short"), NumPut(dy - by, c, cb + 6, "Short")
		, NumPut(dw, c, cb + 8, "Short"), NumPut(dh, c, cb + 10, "Short")
	Return, true
}

-trueski-

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009

I made a similar function that I think works a little cleaner. It will display up to ten levels deep. I could get it to allow more levels, but I don't have the need to right now, and it can get kind of confusing when trying to add more.

Thanks to Polythene for the anchor function.

Syntax:
PrintArray(Array, [DisplayOutput? = 0|1])

Array := Object()
Array[1] := "Test 1"
Array[2] := "Test 2"
Array[3] := Object()
Array[3][1] := "Test 3-1"
Array[3][2] := Object()
Array[3][2][1] := "Test 3-2-1"
Array[3][2][2] := "Test 3-2-2"
Array[3][3] := "Test 3-3"
Array[4] := "Test 4"

PrintArray(Array, 1)
ExitApp

PrintArray(Array, Display=0)
{
	Global PrintArray
	
	If (!IsObject(Array))
	Return ""
	
	Text := "Array" . "`r`n" . "("
	For Key1, Value1 in Array
	{
		If !(IsObject(Value1))
		     Text .= "`r`n`t[" . Key1 . "] => " . Value1
		Else Text .= "`r`n`t[" . Key1 . "] => " . "Array"
		
		If (IsObject(Value1))
		{
			Text .= "`r`n`t" . "("
			
			For Key2, Value2 in Value1
			{
				If !(IsObject(Value2))
		         Text .= "`r`n`t`t[" . Key2 . "] => " . Value2
		    Else Text .= "`r`n`t`t[" . Key2 . "] => " . "Array"
		
				If (IsObject(Value2))
		    {
					Text .= "`r`n`t`t" . "("
					
					For Key3, Value3 in Value2
			    {
						If !(IsObject(Value3))
		             Text .= "`r`n`t`t`t[" . Key3 . "] => " . Value3
		        Else Text .= "`r`n`t`t`t[" . Key3 . "] => " . "Array"
				
						If (IsObject(Value3))
		        {
							Text .= "`r`n`t`t`t" . "("
							
							For Key4, Value4 in Value3
			        {
								If !(IsObject(Value4))
		                 Text .= "`r`n`t`t`t`t[" . Key4 . "] => " . Value4
		            Else Text .= "`r`n`t`t`t`t[" . Key4 . "] => " . "Array"
								
								If (IsObject(Value4))
		            {
									Text .= "`r`n`t`t`t`t" . "("
									
									For Key5, Value5 in Value4
			            {
										If !(IsObject(Value5))
		                     Text .= "`r`n`t`t`t`t`t[" . Key5 . "] => " . Value5
		                Else Text .= "`r`n`t`t`t`t`t[" . Key5 . "] => " . "Array"
								
										If (IsObject(Value5))
		                {
											Text .= "`r`n`t`t`t`t`t" . "("
											
											For Key6, Value6 in Value5
			                {
												If !(IsObject(Value6))
		                         Text .= "`r`n`t`t`t`t`t`t[" . Key6 . "] => " . Value6
		                    Else Text .= "`r`n`t`t`t`t`t`t[" . Key6 . "] => " . "Array"
										
												If (IsObject(Value6))
		                    {
													Text .= "`r`n`t`t`t`t`t`t" . "("
													
													For Key7, Value7 in Value6
			                    {
														If !(IsObject(Value7))
		                             Text .= "`r`n`t`t`t`t`t`t`t[" . Key7 . "] => " . Value7
		                        Else Text .= "`r`n`t`t`t`t`t`t`t[" . Key7 . "] => " . "Array"
														
														If (IsObject(Value7))
		                        {
															Text .= "`r`n`t`t`t`t`t`t`t" . "("
															
															For Key8, Value8 in Value7
			                        {
																If !(IsObject(Value8))
		                                 Text .= "`r`n`t`t`t`t`t`t`t`t[" . Key8 . "] => " . Value8
		                            Else Text .= "`r`n`t`t`t`t`t`t`t`t[" . Key8 . "] => " . "Array"
																
																If (IsObject(Value8))
		                            {
																	Text .= "`r`n`t`t`t`t`t`t`t`t" . "("
																	
																	For Key9, Value9 in Value8
			                            {
																		If !(IsObject(Value9))
		                                     Text .= "`r`n`t`t`t`t`t`t`t`t`t[" . Key9 . "] => " . Value9
		                                Else Text .= "`r`n`t`t`t`t`t`t`t`t`t[" . Key9 . "] => " . "Array"
																		
																		If (IsObject(Value9))
		                                {
																			Text .= "`r`n`t`t`t`t`t`t`t`t`t" . "("
																			
																			For Key10, Value10 in Value9
			                                {
																				If !(IsObject(Value10))
		                                         Text .= "`r`n`t`t`t`t`t`t`t`t`t`t[" . Key10 . "] => " . Value10
		                                    Else Text .= "`r`n`t`t`t`t`t`t`t`t`t`t[" . Key10 . "] => " . "Array"
																			}
																		  
																			Text .= "`r`n`t`t`t`t`t`t`t`t`t" . ")"
																		}
																	}
																	
																	Text .= "`r`n`t`t`t`t`t`t`t`t" . ")"
																}
															}
															
															Text .= "`r`n`t`t`t`t`t`t`t" . ")"
														}
													}
													
													Text .= "`r`n`t`t`t`t`t`t" . ")"
												}
											}
										
										  Text .= "`r`n`t`t`t`t`t" . ")"
										}
									}
								
								  Text .= "`r`n`t`t`t`t" . ")"
								}
							}
						
						  Text .= "`r`n`t`t`t" . ")"
						}
					}
				
				  Text .= "`r`n`t`t" . ")"
				}
			}
		
		  Text .= "`r`n`t" . ")"
		}
	}

  Text .= "`r`n" . ")"
	
	If (!Display)
	Return Text
	
	Gui, PrintArray:+MaximizeBox
	Gui, PrintArray:Add, Edit, x12 y10 w450 h350 vPrintArray ReadOnly, %Text%
  Gui, PrintArray:Show, w476 h374, PrintArray
	Gui, PrintArray:+LastFound
	ControlSend, , {Right}
	WinWaitClose
  Return Text

	PrintArrayGuiSize:
  Anchor("PrintArray", "wh")
  Return

  PrintArrayGuiClose:
  Gui, PrintArray:Destroy
  Return
}

/*
	Function: Anchor
		Defines how controls should be automatically positioned relative to the new dimensions of a window when resized.

	Parameters:
		cl - a control HWND, associated variable name or ClassNN to operate on
		a - (optional) one or more of the anchors: 'x', 'y', 'w' (width) and 'h' (height),
			optionally followed by a relative factor, e.g. "x h0.5"
		r - (optional) true to redraw controls, recommended for GroupBox and Button types

	Examples:
> "xy" ; bounds a control to the bottom-left edge of the window
> "w0.5" ; any change in the width of the window will resize the width of the control on a 2:1 ratio
> "h" ; similar to above but directrly proportional to height

	Remarks:
		To assume the current window size for the new bounds of a control (i.e. resetting) simply omit the second and third parameters.
		However if the control had been created with DllCall() and has its own parent window,
			the container AutoHotkey created GUI must be made default with the +LastFound option prior to the call.
		For a complete example see anchor-example.ahk.

	License:
		- Version 4.60a <http://www.autohotkey.net/~Titan/#anchor>
		- Simplified BSD License <http://www.autohotkey.net/~Titan/license.txt>
*/
Anchor(i, a = "", r = false) {
	static c, cs = 12, cx = 255, cl = 0, g, gs = 8, gl = 0, gpi, gw, gh, z = 0, k = 0xffff
	If z = 0
		VarSetCapacity(g, gs * 99, 0), VarSetCapacity(c, cs * cx, 0), z := true
	If (!WinExist("ahk_id" . i)) {
		GuiControlGet, t, Hwnd, %i%
		If ErrorLevel = 0
			i := t
		Else ControlGet, i, Hwnd, , %i%
	}
	VarSetCapacity(gi, 68, 0), DllCall("GetWindowInfo", "UInt", gp := DllCall("GetParent", "UInt", i), "UInt", &gi)
		, giw := NumGet(gi, 28, "Int") - NumGet(gi, 20, "Int"), gih := NumGet(gi, 32, "Int") - NumGet(gi, 24, "Int")
	If (gp != gpi) {
		gpi := gp
		Loop, %gl%
			If (NumGet(g, cb := gs * (A_Index - 1)) == gp) {
				gw := NumGet(g, cb + 4, "Short"), gh := NumGet(g, cb + 6, "Short"), gf := 1
				Break
			}
		If (!gf)
			NumPut(gp, g, gl), NumPut(gw := giw, g, gl + 4, "Short"), NumPut(gh := gih, g, gl + 6, "Short"), gl += gs
	}
	ControlGetPos, dx, dy, dw, dh, , ahk_id %i%
	Loop, %cl%
		If (NumGet(c, cb := cs * (A_Index - 1)) == i) {
			If a =
			{
				cf = 1
				Break
			}
			giw -= gw, gih -= gh, as := 1, dx := NumGet(c, cb + 4, "Short"), dy := NumGet(c, cb + 6, "Short")
				, cw := dw, dw := NumGet(c, cb + 8, "Short"), ch := dh, dh := NumGet(c, cb + 10, "Short")
			Loop, Parse, a, xywh
				If A_Index > 1
					av := SubStr(a, as, 1), as += 1 + StrLen(A_LoopField)
						, d%av% += (InStr("yh", av) ? gih : giw) * (A_LoopField + 0 ? A_LoopField : 1)
			DllCall("SetWindowPos", "UInt", i, "Int", 0, "Int", dx, "Int", dy
				, "Int", InStr(a, "w") ? dw : cw, "Int", InStr(a, "h") ? dh : ch, "Int", 4)
			If r != 0
				DllCall("RedrawWindow", "UInt", i, "UInt", 0, "UInt", 0, "UInt", 0x0101) ; RDW_UPDATENOW | RDW_INVALIDATE
			Return
		}
	If cf != 1
		cb := cl, cl += cs
	bx := NumGet(gi, 48), by := NumGet(gi, 16, "Int") - NumGet(gi, 8, "Int") - gih - NumGet(gi, 52)
	If cf = 1
		dw -= giw - gw, dh -= gih - gh
	NumPut(i, c, cb), NumPut(dx - bx, c, cb + 4, "Short"), NumPut(dy - by, c, cb + 6, "Short")
		, NumPut(dw, c, cb + 8, "Short"), NumPut(dh, c, cb + 10, "Short")
	Return, true
}

This is what recursion is used for usually ;)

kenn
  • Members
  • 407 posts
  • Last active: Jan 14 2015 08:16 PM
  • Joined: 11 Oct 2010
Is recursive function calls itself?

nimda
  • Members
  • 4368 posts
  • Last active: Aug 09 2015 02:36 AM
  • Joined: 26 Dec 2010
Yes.

trueski
  • Members
  • 121 posts
  • Last active: Jun 25 2014 09:12 PM
  • Joined: 08 Apr 2008

This is what recursion is used for usually ;)


Could you provide an example?
-trueski-

  • Guests
  • Last active:
  • Joined: --

This is what recursion is used for usually ;)


Could you provide an example?

factorial( n ) { ; recursively calculate N!
Return n < 1 ? "" : n < 2 ? n : n * factorial( n - 1 )
}


fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009
Sure, here's a function by Lexikos:
ExploreObj(Obj, NewRow="`n", Equal="  =  ", Indent="`t", Depth=12, CurIndent="") { 

    for k,v in Obj 

        ToReturn .= CurIndent . k . (IsObject(v) && depth>1 ? NewRow . ExploreObj(v, NewRow, Equal, Indent, Depth-1, CurIndent . Indent) : Equal . v) . NewRow 

    return RTrim(ToReturn, NewRow) 

}


trueski
  • Members
  • 121 posts
  • Last active: Jun 25 2014 09:12 PM
  • Joined: 08 Apr 2008

Sure, here's a function by Lexikos:


I've updated my function to make it recursive. Cut out ~120 lines. I also made a few minor modifications to the window. Thanks fragman.

Array := Object()
Array[1] := "Test 1"
Array[2] := "Test 2"
Array[3] := Object()
Array[3][1] := "Test 3-1"
Array[3][2] := Object()
Array[3][2][1] := "Test 3-2-1"
Array[3][2][2] := "Test 3-2-2"
Array[3][3] := "Test 3-3"
Array[4] := "Test 4"

PrintArray(Array)
ExitApp

PrintArray(Array, Display=1, Level=0)
	{
		Global PrintArray
				
		Loop, % 4 + (Level*8)
		Tabs .= A_Space
		
		Output := "Array`r`n" . SubStr(Tabs, 5) . "(" 
		
		For Key, Value in Array
		  {
				If (IsObject(Value))
				  {
            Level++
						Value := PrintArray(Value, 0, Level)
						Level--
					}
				
				Output .= "`r`n" . Tabs . "[" . Key . "] => " . Value
			}
		
		Output .= "`r`n" . SubStr(Tabs, 5) . ")"
		
		
		If (!Display)
	  Return Output
	  
		Gui, PrintArray:+MaximizeBox +Resize
		Gui, PrintArray:Font, s9, Courier New
	  Gui, PrintArray:Add, Edit, x12 y10 w450 h350 vPrintArray ReadOnly HScroll, %Output%
    Gui, PrintArray:Show, w476 h374, PrintArray
	  Gui, PrintArray:+LastFound
	  ControlSend, , {Right}
	  WinWaitClose
    Return Output

	  PrintArrayGuiSize:
    Anchor("PrintArray", "wh")
    Return

    PrintArrayGuiClose:
    Gui, PrintArray:Destroy
    Return
	}

/*
	Function: Anchor
		Defines how controls should be automatically positioned relative to the new dimensions of a window when resized.

	Parameters:
		cl - a control HWND, associated variable name or ClassNN to operate on
		a - (optional) one or more of the anchors: 'x', 'y', 'w' (width) and 'h' (height),
			optionally followed by a relative factor, e.g. "x h0.5"
		r - (optional) true to redraw controls, recommended for GroupBox and Button types

	Examples:
> "xy" ; bounds a control to the bottom-left edge of the window
> "w0.5" ; any change in the width of the window will resize the width of the control on a 2:1 ratio
> "h" ; similar to above but directrly proportional to height

	Remarks:
		To assume the current window size for the new bounds of a control (i.e. resetting) simply omit the second and third parameters.
		However if the control had been created with DllCall() and has its own parent window,
			the container AutoHotkey created GUI must be made default with the +LastFound option prior to the call.
		For a complete example see anchor-example.ahk.

	License:
		- Version 4.60a <http://www.autohotkey.net/~Titan/#anchor>
		- Simplified BSD License <http://www.autohotkey.net/~Titan/license.txt>
*/
Anchor(i, a = "", r = false) {
	static c, cs = 12, cx = 255, cl = 0, g, gs = 8, gl = 0, gpi, gw, gh, z = 0, k = 0xffff
	If z = 0
		VarSetCapacity(g, gs * 99, 0), VarSetCapacity(c, cs * cx, 0), z := true
	If (!WinExist("ahk_id" . i)) {
		GuiControlGet, t, Hwnd, %i%
		If ErrorLevel = 0
			i := t
		Else ControlGet, i, Hwnd, , %i%
	}
	VarSetCapacity(gi, 68, 0), DllCall("GetWindowInfo", "UInt", gp := DllCall("GetParent", "UInt", i), "UInt", &gi)
		, giw := NumGet(gi, 28, "Int") - NumGet(gi, 20, "Int"), gih := NumGet(gi, 32, "Int") - NumGet(gi, 24, "Int")
	If (gp != gpi) {
		gpi := gp
		Loop, %gl%
			If (NumGet(g, cb := gs * (A_Index - 1)) == gp) {
				gw := NumGet(g, cb + 4, "Short"), gh := NumGet(g, cb + 6, "Short"), gf := 1
				Break
			}
		If (!gf)
			NumPut(gp, g, gl), NumPut(gw := giw, g, gl + 4, "Short"), NumPut(gh := gih, g, gl + 6, "Short"), gl += gs
	}
	ControlGetPos, dx, dy, dw, dh, , ahk_id %i%
	Loop, %cl%
		If (NumGet(c, cb := cs * (A_Index - 1)) == i) {
			If a =
			{
				cf = 1
				Break
			}
			giw -= gw, gih -= gh, as := 1, dx := NumGet(c, cb + 4, "Short"), dy := NumGet(c, cb + 6, "Short")
				, cw := dw, dw := NumGet(c, cb + 8, "Short"), ch := dh, dh := NumGet(c, cb + 10, "Short")
			Loop, Parse, a, xywh
				If A_Index > 1
					av := SubStr(a, as, 1), as += 1 + StrLen(A_LoopField)
						, d%av% += (InStr("yh", av) ? gih : giw) * (A_LoopField + 0 ? A_LoopField : 1)
			DllCall("SetWindowPos", "UInt", i, "Int", 0, "Int", dx, "Int", dy
				, "Int", InStr(a, "w") ? dw : cw, "Int", InStr(a, "h") ? dh : ch, "Int", 4)
			If r != 0
				DllCall("RedrawWindow", "UInt", i, "UInt", 0, "UInt", 0, "UInt", 0x0101) ; RDW_UPDATENOW | RDW_INVALIDATE
			Return
		}
	If cf != 1
		cb := cl, cl += cs
	bx := NumGet(gi, 48), by := NumGet(gi, 16, "Int") - NumGet(gi, 8, "Int") - gih - NumGet(gi, 52)
	If cf = 1
		dw -= giw - gw, dh -= gih - gh
	NumPut(i, c, cb), NumPut(dx - bx, c, cb + 4, "Short"), NumPut(dy - by, c, cb + 6, "Short")
		, NumPut(dw, c, cb + 8, "Short"), NumPut(dh, c, cb + 10, "Short")
	Return, true
}

-trueski-