Arrays: Difference between revisions

From AutoIt Wiki
Jump to navigation Jump to search
(WIki cleanup. Removed direct references to more formal general references. Expanded and rewoded the introduction.)
 
(18 intermediate revisions by 6 users not shown)
Line 1: Line 1:
An '''array''' is an container data structure which stores elements (variables) aligned in the computer's memory which are referenced by the array's [[identifier]] and an index specifying a desired element.  This sounds complex, so, why should you learn about arrays?  The short answer is that the array data structure is fundamental to effective programming.  This tutorial targets people who are beginners.  To understand how arrays work, it is imperative to try out and modify the provided samples.  Make sure that each concept is understood before the next concept is attempted.  This tutorial assumes the use of the SciTE editor.  There is a minimal version of SciTE included in the latest stable release of AutoIt (version 3.2.0.1 and above).
An '''array''' is a [[Data_Structures|data structure]] which stores elements (variables) aligned in a computer's memory.  Arrays are referenced by an [[identifier]] (variable name) and an index specifying a desired element.  The array concept seems complex but the concept can be easily grasped.  Why are arrays so important?  The array is a fundamental data structure found in most programming languages.   
 
This tutorial targets people who are beginners.  To understand how arrays work, it is imperative to try out and modify the provided samples.  Make sure that each concept is understood before the next concept is attempted.  This tutorial assumes the use of the SciTE editor.  There is a minimal version of SciTE included in the latest stable release of AutoIt (version 3.2.0.1 and above).


== Declaring Arrays in AutoIt ==
== Declaring Arrays in AutoIt ==


You declare an array in the same manner as you would declare any variable in AutoIt. But when you know it is an array, you add information on how many elements you want to have in the array. This information is provided by adding brackets after the identifier and a number indicating how many elements you want the array to hold.  
An array is declared in the same manner as a variable in AutoIt. The difference is that for an array, extra information on how many elements are to be included in the array must be specified. This information is provided by adding brackets after the identifier and a number indicating how many elements the array will possess.  


<syntaxhighlight lang=autoit>
<syntaxhighlight lang='autoit'>
Global $arr[4] ; Will make space for 4 elements.
Global $arr[4] ; Will make space for 4 elements.
Local  $arr[1] ; Will make space for 1 element.
Local  $arr[1] ; Will make space for 1 element.
;NOTE! Avoid using Dim.  Use Global or Local instead.
Dim    $arr[3] ; Will make space for 3 elements.  Note: Avoid using Dim.  Use Global or Local instead.
Dim    $arr[3] ; Will make space for 3 elements.  
</syntaxhighlight>  
</syntaxhighlight>


Making space for one element may seem ridiculous, but further down the road we shall see how we can have arrays grow and shrink.  So when we declare an array with one element, it is usually just a place holder.  
In AutoIt, a variable may be converted to an array by either using the {{Help File|ReDim}} keyword or assigning a function which returns an array to the variable.


In AutoIt, you can also declare an array as a normal variable and later get an array assigned to it from a function.  Note about the "from a function" part: this can refer to a user-defined function or an AutoIt internal built-in function.  The point is, a variable does not need to hold an array before the array is assigned to it.
For example, the function {{Help File|StringSplit}} returns an array which will be assigned to $arr.


<syntaxhighlightlang=autoit>
<syntaxhighlight lang='autoit'>
Local $arr = StringSplit("This is my string. I want to split it in sentences.", ".")
Local $arr = StringSplit("This is my string. I want to split it in sentences.", '.')
</syntaxhighlight>
</syntaxhighlight>


Now to make really certain we have an array from {{Help File|StringSplit}}, we should check it with the {{Help File|IsArray}} built-in function.
Now to make really certain we have an array from {{Help File|StringSplit}}, we should check it with the {{Help File|IsArray}} built-in function.


<syntaxhighlightlang=autoit>
<syntaxhighlight lang='autoit'>
If IsArray($arr) Then  
If IsArray($arr) Then  
     ; Do work on the array
     ; Do work on the array
EndIf
EndIf
</syntaxhighlight>
</syntaxhighlight>


Line 32: Line 33:
When we declare the array we make some room in memory for future data.  We want to assign some data to the items in the array.  Now here is the catch. The array always starts at index zero.  So, the first element in the array will be accessed by zero, the second element in the array is accessed at by one and so on.
When we declare the array we make some room in memory for future data.  We want to assign some data to the items in the array.  Now here is the catch. The array always starts at index zero.  So, the first element in the array will be accessed by zero, the second element in the array is accessed at by one and so on.


<syntaxhighlightlang=autoit>
<syntaxhighlight lang='autoit'>
  Local $arr[3] ; Make room for three elements
  Local $arr[3] ; Make room for three elements
  ;Assign some data
  ;Assign some data
  $arr[0]="Element 1"
  $arr[0] = "Element 1"
  $arr[1]="Element 2"
  $arr[1] = "Element 2"
  $arr[2]="Element 3"
  $arr[2] = "Element 3"
</syntaxhighlight>
</syntaxhighlight>


You can also assign all the data in one smack like this:
You can also assign all the data in one smack like this:


<syntaxhighlightlang=autoit>
<syntaxhighlight lang='autoit'>
Local $arr[3] = ["element 1", "element 2", "element 3"]
Local $arr[3] = ["element 1", "element 2", "element 3"]
</syntaxhighlight>
</syntaxhighlight>


Line 54: Line 55:
Let's walk all elements in the previous sample:
Let's walk all elements in the previous sample:


<syntaxhighlightlang=autoit>
<syntaxhighlight lang='autoit'>
Local $arr[3] = ["element 1", "element 2", "element 3"]  
Local $arr[3] = ["element 1", "element 2", "element 3"]  


For $i = 0 to 3 - 1 ; We have an array with three elements but the last index is two.
For $i = 0 to 3 - 1 ; We have an array with three elements but the last index is two.
    ConsoleWrite($arr[$i] & @LF)
    ConsoleWrite($arr[$i] & @LF)
Next
Next
</syntaxhighlight>
</syntaxhighlight>


=== Determine array size with UBound ===
=== Determine Array Size With UBound ===


The "3 - 1" construct used in the last sample looked strange.  It is not a good idea to hard-code size like that. So lets improve our sample a little.  
The "3 - 1" construct used in the last sample looked strange.  It is not a good idea to hard-code size like that. So lets improve our sample a little.  


<syntaxhighlightlang=autoit>
<syntaxhighlight lang='autoit'>
Local $iMax = 3
Local $iMax = 3
Local $arr[$iMax] = ["Element 1", "Element 2", "Element 3"]
 
For $i = 0 to $iMax - 1
Local $arr[$iMax] = ["Element 1", "Element 2", "Element 3"]
    ConsoleWrite($arr[$i] & @LF)
 
Next
For $i = 0 to $iMax - 1
    ConsoleWrite($arr[$i] & @LF)
Next
</syntaxhighlight>
</syntaxhighlight>


Line 77: Line 80:
But say you don't know the size of the array upfront because it may come in a variable size when created dynamically.
But say you don't know the size of the array upfront because it may come in a variable size when created dynamically.


<syntaxhighlightlang=autoit>
<syntaxhighlight lang='autoit'>
Local $iMax  
Local $iMax  


Local $data="Element 1|Element 2|Element 3"  
Local $data = "Element 1|Element 2|Element 3"  


; The string in data will be split into an array everywhere | is encountered  
; The string in data will be split into an array everywhere | is encountered  
Line 96: Line 99:
</syntaxhighlight>
</syntaxhighlight>


When you run the above code you will see that $iMax is four and not three as you might have expected. The reason for this is that the developer of the StringSplit() function thought it was a good idea to use the first item (item zero) to keep a count of valid items in the array. This makes sense in many situations as you now have an array containing data with an index starting at one. So our sample code can now be rewritten like this.
When you run the above code you will see that $iMax is four and not three as you might have expected. The reason for this is that the developer of the {{Help File|StringSplit}} function thought it was a good idea to use the first item (item zero) to keep a count of valid items in the array. This makes sense in many situations as you now have an array containing data with an index starting at one. So our sample code can now be rewritten like this.


<syntaxhighlightlang=autoit>
<syntaxhighlight lang='autoit'>
Local $iMax
Local $iMax


Local $data = "Element 1|Element 2|Element 3"
Local $data = "Element 1|Element 2|Element 3"


; The string in data will be split into an array everywhere | is encountered
; The string in data will be split into an array everywhere | is encountered
Local $arr = StringSplit($data, "|")   
Local $arr = StringSplit($data, "|")   


If IsArray($arr) Then  
If IsArray($arr) Then  
    For $i = 1 to $arr[0]
    For $i = 1 to $arr[0]
        ConsoleWrite($arr[$i] & @LF)
        ConsoleWrite($arr[$i] & @LF)
    Next
    Next
EndIf
EndIf
</syntaxhighlight>
</syntaxhighlight>


There is another good reason for keeping the count in $arr[0]. When you start to use arrays extensively, you will encounter situations where you have to create an array without knowing how many of the elements you will use. Resizing the array is a relatively expensive operation (in CPU cycles) in most languages.
There is another good reason for keeping the count in $arr[0]. When you start to use arrays extensively, you will encounter situations where you have to create an array without knowing how many of the elements you will use. Resizing the array is a relatively expensive operation (in CPU cycles) in most languages.


Now consider our example if our initial array has reserved space for ten items but we end up only using three. In this case iterating the array using UBound will force us to check for empty elements. While iterating with $arr[0] needs no other change than maintaining the correct count in $arr[0].
Now consider our example if our initial array has reserved space for ten items but we end up only using three. In this case iterating the array using {{Help File|UBound}} will force us to check for empty elements. While iterating with $arr[0] needs no other change than maintaining the correct count in $arr[0].


<syntaxhighlightlang=autoit>
<syntaxhighlight lang='autoit'>
Local $iMax=10
Local $iMax=10


;NOTE: We have added the count in the first element
;NOTE: We have added the count in the first element


Local $arr[$iMax] = [3, "Element 1", "Element 2", "Element 3"]
Local $arr[$iMax] = [3, "Element 1", "Element 2", "Element 3"]


;NOTE: We use the count in $arr[0] to indicate the last item and we start from index=1
;NOTE: We use the count in $arr[0] to indicate the last item and we start from index=1
For $i = 1 to $arr[0]
For $i = 1 to $arr[0]
    ConsoleWrite($arr[$i] & @LF)
    ConsoleWrite($arr[$i] & @LF)
Next
Next
</syntaxhighlight>
</syntaxhighlight>


== Changing array sizes with ReDim ==
== Changing Array Sizes With ReDim ==


As arrays are critical to algorithm implementations, and in AutoIt even more so as there is no other means of grouping data, we have to understand how to let it grow and shrink. This is where the keyword ReDim comes into the picture.
As arrays are critical to algorithm implementations, and in AutoIt even more so as there is no other means of grouping data, we have to understand how to let it grow and shrink. This is where the keyword {{Help File|ReDim}} comes into the picture.


Let's make an example. We want our array to hold data lines but we don't know how many items we need. We make a guess, in this case five. Now, we use that array to hold data we get from a loop with a random number of iterations. If the array is too small it should automatically be increased. Before we dump the array to output, we should adjust it to the exact size it is supposed to be.
Let's make an example. We want our array to hold data lines but we don't know how many items we need. We make a guess, in this case five. Now, we use that array to hold data we get from a loop with a random number of iterations. If the array is too small it should automatically be increased. Before we dump the array to output, we should adjust it to the exact size it is supposed to be.
Line 146: Line 149:


For $i = 1 to $iRandom
For $i = 1 to $iRandom
; Check that the array is big enough
    ; Check that the array is big enough
If UBound($arr) = $i Then
    If UBound($arr) = $i Then
; Resize the array when $i is equal to the element count in the array to prevent subscript error
        ; Resize the array when $i is equal to the element count in the array to prevent subscript error
ReDim $arr[$arr[0] + $iMax]
        ReDim $arr[$arr[0] + $iMax]
EndIf
    EndIf


$arr[$i] = "Item " & $i ; safely add data to new index element
    $arr[$i] = "Item " & $i ; safely add data to new index element


$arr[0] = $i ; update the index count for future reference
    $arr[0] = $i ; update the index count for future reference
Next
Next


Line 163: Line 166:
; Now dump the results
; Now dump the results
For $i = 1 to $arr[0]
For $i = 1 to $arr[0]
ConsoleWrite("$arr[" & $i & "]:=" & $arr[$i] &  @LF)
    ConsoleWrite("$arr[" & $i & "]:=" & $arr[$i] &  @LF)
Next
Next


Line 172: Line 175:
Note how the array now has first been adjusted to a multiple of $iMax and in the last part adjusted down to a size matching the data items.
Note how the array now has first been adjusted to a multiple of $iMax and in the last part adjusted down to a size matching the data items.


== Multi dimensional arrays ==
== Passing Arrays to Functions ==
 
Now what is a good explanation of an multi-dimensional array?
It could be a table where you access one item in the table at a time.
 
<syntaxhighlight lang="autoit">
Local  $arr[3][3] = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
 
For $i = 0 to UBound( $arr, 1) - 1
  For $j = 0 to UBound($arr, 2) - 1
  ConsoleWrite("$arr[" & $i & "][" & $j & "]:=" & $arr[$i][$j] & @LF)
  Next
 
  ConsoleWrite(@LF)
Next
</syntaxhighlight>


You can add a number of dimensions not to exceed 64 as stated in the help file section "AutoIt>Appendix>AutoIt3 limits/Defaults".  
There is no special syntax required to pass an array as a function argument unlike a low level language such as C.
Drop me a note if you encounter any circumstances where it might be simpler to add dimensions rather than using other techniques.
The following example demonstrates:


Here is a four dimensional sample. You tell me how that initializer is for readability.
<syntaxhighlight lang="AutoIt">
Local Const $myArray[5] = [1, 2, 3, 4, 5]


<syntaxhighlight lang="autoit">
displayArray($myArray)
; NOTE: The following is supposed to be all on one line
; but we use the "_" character to split it into multiple lines for readability


Local $arr[3][3][3][3] = [_
Func displayArray(Const $array)
[[[1, 2, 3], [2, 3, 4], [3, 4, 5]], [[1, 2, 3], [2, 3, 4], [3, 4, 5]], [[1, 2, 3], [2, 3, 4], [3, 4, 5]]], _
    Local Const $arrayLength = UBound($array)
[[[1, 2, 3], [2, 3, 4], [3, 4, 5]], [[1, 2, 3], [2, 3, 4], [3, 4, 5]], [[1, 2, 3], [2, 3, 4], [3, 4, 5]]], _
[[[1, 2, 3], [2, 3, 4], [3, 4, 5]], [[1, 2, 3], [2, 3, 4], [3, 4, 5]], [[1, 2, 3], [2, 3, 4], [3, 4, 5]]]]


For $i = 0 To UBound($arr, 1) - 1
    For $i = 0 To $arrayLength - 1
For $j = 0 To UBound($arr, 2) - 1
        MsgBox($MB_OK, "displayArray", $array[$i])
For $k = 0 To UBound($arr, 3) - 1
    Next
For $l = 0 To UBound($arr, 4) - 1
EndFunc
ConsoleWrite("$arr[" & $i & "][" & $j & "][" & $k & "][" & $l & "]:=" & $arr[$i][$j][$k][$l] & @LF)
Next
Next
Next
Next
 
ConsoleWrite(@LF)
</syntaxhighlight>
</syntaxhighlight>


== Arrays in Arrays ==
You can not declare the variable to hold the array in the function declaration as an array. So, users could pass on a variable. So you have to check that the variable holds an array before you do array specific operations on it.
The following code example will cause an error:


You may save an array in an array element (item). The thing is, there is no way to directly access that array stored in the element. You have to go through a variable to get access to the embedded array which may make your code overly complicated and difficult to debug.
<syntaxhighlight lang="AutoIt">
Local Const $myArray[5] = [1, 2, 3, 4, 5]


Remember that there may be issues if you pass an array containing arrays into a function and the embedded array is changed inside that function. So, to conclude this, try not to embed arrays within arrays unless you absolutely need to and are prepared to do rigorous testing to make sure your code will always work as expected.
displayArray($myArray)


That said, here is an example.
Func displayArray(Const $array[5])
    Local Const $arrayLength = UBound($array)


<syntaxhighlight lang="autoit">
    For $i = 0 To $arrayLength - 1
Local $arr[3]
         MsgBox($MB_OK, "displayArray", $array[$i])
Local $a1[3] = [2, "a1-1","a1-2"]
    Next
Local $a2[3] = [2, "a2-1","a2-2"]
EndFunc
$arr[1] = $a1
$arr[2] = $a2
$arr[0] = 2
Local $dumy
 
For $i = 1 to $arr[0]
    $dumy = $arr[$i]
 
    If IsArray($dumy) Then
         For $j = 1 to $dumy[0]
            ConsoleWrite("$i:=" & $i & ", $dumy[" & $j & "]:=" & $dumy[$j] & @LF)
        Next
    Else
        ConsoleWrite("!>Oops!, What happened? Expected an array!" & @LF)
    EndIf
Next
</syntaxhighlight>
</syntaxhighlight>
== Passing arrays to a function ==
There is no hocus pocus about this. Only a few rules.
* You can not declare the variable to hold the array in the function declaration as an array. So, users could pass on a variable. So you have to check that the variable holds an array before you do array specific operations on it.
* You don't have to, but you should, specify the variable to hold the array as ByRef.


During the tutorial you have probably noticed that there is a lot of code that is equal in each sample. I'm especially thinking about the code we have used to output the array content. Let's make life easier and create a debug function.
During the tutorial you have probably noticed that there is a lot of code that is equal in each sample. I'm especially thinking about the code we have used to output the array content. Let's make life easier and create a debug function.
Line 276: Line 232:
  Func ArrayFiller(ByRef $arr)
  Func ArrayFiller(ByRef $arr)
     If IsArray($arr) Then  
     If IsArray($arr) Then  
         ReDim $arr[3] ;Notice we might discard content in this operation
         ReDim $arr[3] ; Notice we might discard content in this operation
     Else
     Else
         Local $foo[3]
         Local $foo[3]
         $arr = $foo
         $arr = $foo
     EndIf  
     EndIf  
     ;Fill the array
     ;Fill the array
     $arr[0] = 2
     $arr[0] = 2
Line 302: Line 259:
</syntaxhighlight>
</syntaxhighlight>


== Passing arrays from a function ==
== Returning Arrays From Functions ==


As you could observe, in the previous samples, an array will be passed back and forth with the ByRef keyword infront of the variable holding the array in the function declaration.
As you could observe, in the previous samples, an array will be passed back and forth with the ByRef keyword infront of the variable holding the array in the function declaration.
Line 323: Line 280:
</syntaxhighlight>
</syntaxhighlight>


== Forum FAQ about arrays ==
== Comparing Arrays ==
 
'''You can not compare complete arrays:'''<br/>
<syntaxhighlight lang="autoit">
Local $Array1[3] = [1, 2, 3]
Local $Array2[3] = [1, 2, 3]
Local $Array3 = $Array1


Feel free to add questions and answers you have encountered in the forum.
ConsoleWrite("1) " & ($Array1 = $Array2 ? True : False) & @LF)
; while they contain the same data, the comparison does not work.


=== Accessing Arrays ===
ConsoleWrite("2) " & ($Array1 = $Array3 ? True : False) & @LF)
; even though they are referencing the same array, the comparison does not work.
</syntaxhighlight>


Obviously one can assign arrays to variables:
Comparing arrays will always return false since the array memory addresses are always different for different arrays.  You have to instead, compare all elements one after the other. It might be a good idea to first compare array sizes if that can vary for both the compared arrays!


The next example creates an array from user input and then compares elements from a 1D Array to the first column of a 2D array to return the matches as words.
<syntaxhighlight lang="autoit">
<syntaxhighlight lang="autoit">
$NewArray = $OldArray
; sample number string split and convert to words
; uncomment the ConsoleWrite and _ArrayDisplay lines to see what it does
;# some includes
#include <Array.au3>
 
;# some variables
 
;Create 2D array to display
Local $aWords[10][2] = [[0, 'nil'] _
, [1, 'one'] _
, [2, 'two'] _
, [3, 'three'] _
, [4, 'four'] _
, [5, 'five'] _
, [6, 'six'] _
, [7, 'seven'] _
, [8, 'eight'] _
, [9, 'nine']] ; :)
 
;~ _ArrayDisplay($aWords)
 
; the order of the sample digits is to show that the comparison below is accurate, from left to right
Local $Originalinput = InputBox("Type a number", "A number?","97524601")
 
Local $userinput = $Originalinput ; pass the orginal input to a second var
; now parse the userinput to that we know what was submitted, then repeat in words
; first strip all white space
$userinput = StringStripWS($userinput, 8)
 
If StringIsDigit($userinput) Then ; continue
 
; now get the length of the string
Local $islong = StringLen($userinput)
 
; make an array with size = length to insert items into
Local $aInput[1] ; create blank one dimensional array
;~ _ArrayDisplay($aInput)
; eat the string one digit at a time :)
Local $oneleft
For $i = 1 To $islong Step 1
$oneleft = StringLeft($userinput, 1)
ConsoleWrite("$oneleft: " & $oneleft & @CRLF)
$userinput = StringTrimLeft($userinput, 1) ; make the $userinput one shorter on each loop
_ArrayAdd($aInput, $oneleft, 1)
Next
;~ _ArrayDisplay($aInput)
; now write out what the user typed in words
 
;~ ConsoleWrite("How long is the string: " & $islong & @CRLF)
; compare the user input to our defined array
For $k = 0 To $islong Step 1
For $j = 0 To (UBound($aWords) - 1) Step 1
;~ ConsoleWrite("Compare $j " & $aWords[$j][0] & " to $k " & $aInput[$k] & @CRLF)
If $aWords[$j][0] == $aInput[$k] Then
MsgBox(0, "Number to word", $Originalinput&@CRLF&"User typed "& $aInput[$k]& @CRLF & $aWords[$j][1] & @CRLF,1)
EndIf
;~ ConsoleWrite("For... Next inner loops: " & $k & @CRLF)
Next
;~ ConsoleWrite("For... Next outer loops: " & $j & @CRLF)
Next
 
EndIf ; if its not digits, skip
 
</syntaxhighlight>
</syntaxhighlight>


One can also assign single array elements:
== Multi Dimensional Arrays ==
 
Now what is a good explanation of a multi-dimensional array?
It could be a table where you access one item in the table at a time.


<syntaxhighlight lang="autoit">
<syntaxhighlight lang="autoit">
$Array[1] = "Element 1"
Local  $arr[3][3] = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
 
For $i = 0 to UBound( $arr, 1) - 1
    For $j = 0 to UBound($arr, 2) - 1
        ConsoleWrite("$arr[" & $i & "][" & $j & "]:=" & $arr[$i][$j] & @LF)
    Next
 
    ConsoleWrite(@LF)
Next
</syntaxhighlight>
</syntaxhighlight>


Or
You can add a number of dimensions not to exceed sixty-four as stated in the help file section [http://www.autoitscript.com/autoit3/docs/appendix/LimitsDefaults.htm AutoIt3 limits/Defaults].


<syntaxhighlight lang="autoit">
Here is a four dimensional example. You tell me how that initializer is for readability.
$Content = $Array[1]
</syntaxhighlight>


=== Comparing Arrays ===
<syntaxhighlight lang="autoit">
; NOTE: The following is supposed to be all on one line
; but we use the "_" character to split it into multiple lines for readability


'''You can not, however, compare complete arrays:'''<br/>
Local $arr[3][3][3][3] = _
<syntaxhighlight lang="autoit">
[ _
Local $Array1[3] = [1, 2, 3]
[ _
Local $Array2[3] = [1, 2, 3]
[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
Local $Array3[4] = [1, 2, 3, 4]
[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
[[1, 2, 3], [2, 3, 4], [3, 4, 5]] _
], _
[ _
[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
[[1, 2, 3], [2, 3, 4], [3, 4, 5]] _
], _
[ _
[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
[[1, 2, 3], [2, 3, 4], [3, 4, 5]] _
] _
]


If $Array1 == $Array2 Then ConsoleWrite("1.) $Array1 is equal to $Array2! which might be correct in some sense." & @LF); while they contain the same data, the comparison does not work.
For $i = 0 To UBound($arr, 1) - 1
For $j = 0 To UBound($arr, 2) - 1
For $k = 0 To UBound($arr, 3) - 1
For $l = 0 To UBound($arr, 4) - 1
ConsoleWrite("$arr[" & $i & "][" & $j & "][" & $k & "][" & $l & "]:=" & $arr[$i][$j][$k][$l] & @LF)
Next
Next
Next
Next


If $Array1 == $Array3 Then ConsoleWrite("2.) $Array1 is equal to $Array3! which is incorrect." & @LF); even though they're different in size, it's incorrectly determined that they're equal.
ConsoleWrite(@LF)
</syntaxhighlight>
</syntaxhighlight>


I have the impression that such comparisons compare the memory address of the arrays instead of the array elements values. And the addresses are always different for different arrays.  You have to instead, compare all elements one after the other. It might be a good idea to first compare array sizes if that can vary for both the compared arrays!
== Arrays in Arrays ==


=== Array UDF: array.au3 ===
You may save an array in an array element (item). Remember that there may be issues if you pass an array containing arrays into a function and the embedded array is changed inside that function. So, as a general rule, try not to embed arrays within arrays unless you absolutely need to and are prepared to do rigorous testing to make sure your code will always work as expected.
 
You can access the elements of these embedded arrays directly (note the additional () required):
 
<syntaxhighlight lang="autoit">
Local $aContainerArray[2]
Local $aInternalArray_0[2] = ["Internal-0-0","Internal-0-1"]
Local $aInternalArray_1[2] = ["Internal-1-0","Internal-1-1"]
$aContainerArray[0] = $aInternalArray_0
$aContainerArray[1] = $aInternalArray_1
 
ConsoleWrite("1 element of InternalArray_0: " & ($aContainerArray[0])[1] & @CRLF)
ConsoleWrite("0 element of InternalArray_1: " & ($aContainerArray[1])[0] & @CRLF)
</syntaxhighlight>


AutoIt features a large list of User-Defined Functions (UDF), among which is a module supplying extra array functions. You can find a reference on those functions in AutoIt's Help file as the last main chapter named 'User Defined Functions Reference'.
== More Information ==


AutoIt features a large list of [http://www.autoitscript.com/autoit3/docs/libfunctions.htm User Defined Functions (UDF)], among which is a module supplying extra array functions. You can find a reference on those functions in AutoIt's Help file as the last main chapter named [http://www.autoitscript.com/autoit3/docs/libfunctions.htm User Defined Functions Reference].
<br />
<br />
An associative array is an array where the keys (index numbers) are string instead of integer, as they are in AutoIt. See the [[Associative Arrays]] page for more information.
[[Category:Tutorials]]
[[Category:Tutorials]]

Latest revision as of 13:33, 13 January 2024

An array is a data structure which stores elements (variables) aligned in a computer's memory. Arrays are referenced by an identifier (variable name) and an index specifying a desired element. The array concept seems complex but the concept can be easily grasped. Why are arrays so important? The array is a fundamental data structure found in most programming languages.

This tutorial targets people who are beginners. To understand how arrays work, it is imperative to try out and modify the provided samples. Make sure that each concept is understood before the next concept is attempted. This tutorial assumes the use of the SciTE editor. There is a minimal version of SciTE included in the latest stable release of AutoIt (version 3.2.0.1 and above).

Declaring Arrays in AutoIt

An array is declared in the same manner as a variable in AutoIt. The difference is that for an array, extra information on how many elements are to be included in the array must be specified. This information is provided by adding brackets after the identifier and a number indicating how many elements the array will possess.

Global $arr[4] ; Will make space for 4 elements.
Local  $arr[1] ; Will make space for 1 element.
Dim    $arr[3] ; Will make space for 3 elements.  Note: Avoid using Dim.  Use Global or Local instead.

In AutoIt, a variable may be converted to an array by either using the ReDim keyword or assigning a function which returns an array to the variable.

For example, the function StringSplit returns an array which will be assigned to $arr.

Local $arr = StringSplit("This is my string. I want to split it in sentences.", '.')

Now to make really certain we have an array from StringSplit, we should check it with the IsArray built-in function.

If IsArray($arr) Then 
     ; Do work on the array
EndIf

Assigning Data to Array Elements

When we declare the array we make some room in memory for future data. We want to assign some data to the items in the array. Now here is the catch. The array always starts at index zero. So, the first element in the array will be accessed by zero, the second element in the array is accessed at by one and so on.

 Local $arr[3] ; Make room for three elements
 ;Assign some data
 $arr[0] = "Element 1"
 $arr[1] = "Element 2"
 $arr[2] = "Element 3"

You can also assign all the data in one smack like this:

Local $arr[3] = ["element 1", "element 2", "element 3"]

This zero-based indexing is quite common in most computer languages, but it can be a source of headaches to beginners until it becomes second nature to them. For example, every time you want to loop through a range of elements and the range includes the last element, you have to subtract one from the number of items your array is holding, to get the index of the last item. I.E., An array with three elements has a last index of two.

So if you don't take zero-based indexing into consideration in your code, you may ask for something outside the memory area set aside for the array. When you do, you get an error message (Array variable has incorrect number of subscripts or subscript dimension range exceeded) and your script will cease execution.

Accessing Data in Arrays

Let's walk all elements in the previous sample:

Local $arr[3] = ["element 1", "element 2", "element 3"] 

For $i = 0 to 3 - 1 ; We have an array with three elements but the last index is two.
    ConsoleWrite($arr[$i] & @LF)
Next

Determine Array Size With UBound

The "3 - 1" construct used in the last sample looked strange. It is not a good idea to hard-code size like that. So lets improve our sample a little.

Local $iMax = 3

Local $arr[$iMax] = ["Element 1", "Element 2", "Element 3"]

For $i = 0 to $iMax - 1
    ConsoleWrite($arr[$i] & @LF)
Next

Now that's a bit cleaner. It's also a lot easier to increase or decrease the size of the array.
But say you don't know the size of the array upfront because it may come in a variable size when created dynamically.

Local $iMax 

Local $data = "Element 1|Element 2|Element 3" 

; The string in data will be split into an array everywhere | is encountered 
Local $arr = StringSplit($data, "|") 

If IsArray($arr) Then
     $iMax = UBound($arr); get array size

     ConsoleWrite("Items in the array: " & $iMax & @LF)

     For $i = 0 to $iMax - 1; subtract 1 from size to prevent an out of bounds error
         ConsoleWrite($arr[$i] & @LF)
     Next
EndIf

When you run the above code you will see that $iMax is four and not three as you might have expected. The reason for this is that the developer of the StringSplit function thought it was a good idea to use the first item (item zero) to keep a count of valid items in the array. This makes sense in many situations as you now have an array containing data with an index starting at one. So our sample code can now be rewritten like this.

Local $iMax

Local $data = "Element 1|Element 2|Element 3"

; The string in data will be split into an array everywhere | is encountered
Local $arr = StringSplit($data, "|")  

If IsArray($arr) Then 
    For $i = 1 to $arr[0]
        ConsoleWrite($arr[$i] & @LF)
    Next
EndIf

There is another good reason for keeping the count in $arr[0]. When you start to use arrays extensively, you will encounter situations where you have to create an array without knowing how many of the elements you will use. Resizing the array is a relatively expensive operation (in CPU cycles) in most languages.

Now consider our example if our initial array has reserved space for ten items but we end up only using three. In this case iterating the array using UBound will force us to check for empty elements. While iterating with $arr[0] needs no other change than maintaining the correct count in $arr[0].

Local $iMax=10

;NOTE: We have added the count in the first element

Local $arr[$iMax] = [3, "Element 1", "Element 2", "Element 3"]

;NOTE: We use the count in $arr[0] to indicate the last item and we start from index=1
For $i = 1 to $arr[0]
    ConsoleWrite($arr[$i] & @LF)
Next

Changing Array Sizes With ReDim

As arrays are critical to algorithm implementations, and in AutoIt even more so as there is no other means of grouping data, we have to understand how to let it grow and shrink. This is where the keyword ReDim comes into the picture.

Let's make an example. We want our array to hold data lines but we don't know how many items we need. We make a guess, in this case five. Now, we use that array to hold data we get from a loop with a random number of iterations. If the array is too small it should automatically be increased. Before we dump the array to output, we should adjust it to the exact size it is supposed to be.

Local Const $iMax = 5

; NOTE: We have added the count in the first element
Local $arr[$iMax] = [0] ; Initiate the array and place a counter in the first element.

; Generate a random number between 0 and 20
Local Const $iRandom = Random(0, 20, 1)

For $i = 1 to $iRandom
    ; Check that the array is big enough
    If UBound($arr) = $i Then
        ; Resize the array when $i is equal to the element count in the array to prevent subscript error
        ReDim $arr[$arr[0] + $iMax]
    EndIf

    $arr[$i] = "Item " & $i ; safely add data to new index element

    $arr[0] = $i ; update the index count for future reference
Next

; Adjust the array size. This time it is probably downward to the size of
; $arr[0] + 1 (remember the first item is $arr[0])
ReDim $arr[$arr[0] + 1] 

; Now dump the results
For $i = 1 to $arr[0]
    ConsoleWrite("$arr[" & $i & "]:=" & $arr[$i] &  @LF)
Next

; Visually check that the values are sound
ConsoleWrite("Ubound($arr):=" & UBound($arr) & ", $arr[0]:=" & $arr[0] & ", $iRandom:=" & $iRandom & @LF)

Note how the array now has first been adjusted to a multiple of $iMax and in the last part adjusted down to a size matching the data items.

Passing Arrays to Functions

There is no special syntax required to pass an array as a function argument unlike a low level language such as C. The following example demonstrates:

Local Const $myArray[5] = [1, 2, 3, 4, 5]

displayArray($myArray)

Func displayArray(Const $array)
    Local Const $arrayLength = UBound($array)

    For $i = 0 To $arrayLength - 1
        MsgBox($MB_OK, "displayArray", $array[$i])
    Next
EndFunc

You can not declare the variable to hold the array in the function declaration as an array. So, users could pass on a variable. So you have to check that the variable holds an array before you do array specific operations on it. The following code example will cause an error:

Local Const $myArray[5] = [1, 2, 3, 4, 5]

displayArray($myArray)

Func displayArray(Const $array[5])
    Local Const $arrayLength = UBound($array)

    For $i = 0 To $arrayLength - 1
        MsgBox($MB_OK, "displayArray", $array[$i])
    Next
EndFunc

During the tutorial you have probably noticed that there is a lot of code that is equal in each sample. I'm especially thinking about the code we have used to output the array content. Let's make life easier and create a debug function.

 Func dbgArray(ByRef $arr, $msg="")
     If $msg <> "" Then 
         ConsoleWrite("*** " & $msg & " ***" & @LF)
     EndIf

     For $i = 0 to UBound($arr) - 1
        ConsoleWrite("$arr[" & $i & "]:=" & $arr[$i] &  @LF)
     Next

     ConsoleWrite( "Ubound($arr)=:=" & UBound($arr) & ", $arr[0]:=" & $arr[0] & @LF)   
 EndFunc

And let's make a little function to fill our arrays with something. Note how the ArrayFiller makes sure it works on an array.

 Func ArrayFiller(ByRef $arr)
     If IsArray($arr) Then 
         ReDim $arr[3] ; Notice we might discard content in this operation
     Else
         Local $foo[3]
         $arr = $foo
     EndIf 

     ;Fill the array
     $arr[0] = 2
     $arr[1] = "Fill 1"
     $arr[2] = "Fill 2"
 EndFunc

And finally some code using the new functions

   Local $arr1[1]
   ArrayFiller($arr1)
   dbgArray($arr1)

This code is a test on what happens when we pass a regular variable.

   Local $arr2
   ArrayFiller($arr2)
   dbgArray($arr2)

Returning Arrays From Functions

As you could observe, in the previous samples, an array will be passed back and forth with the ByRef keyword infront of the variable holding the array in the function declaration.

We could also have used the Return keyword in a function. Lets re-work the ArrayFiller function to do this rather than using a variable ByRef.

 Func ArrayFiller2()
   Local $arr = [3, "Fill 1", "Fill 2"]
   Return $arr
 EndFunc

And now we can use the function like this

 Local $foo = ArrayFiller2()
 dbgArray($foo)

Comparing Arrays

You can not compare complete arrays:

Local $Array1[3] = [1, 2, 3]
Local $Array2[3] = [1, 2, 3]
Local $Array3 = $Array1

ConsoleWrite("1) " & ($Array1 = $Array2 ? True : False) & @LF)
; while they contain the same data, the comparison does not work.

ConsoleWrite("2) " & ($Array1 = $Array3 ? True : False) & @LF)
; even though they are referencing the same array, the comparison does not work.

Comparing arrays will always return false since the array memory addresses are always different for different arrays. You have to instead, compare all elements one after the other. It might be a good idea to first compare array sizes if that can vary for both the compared arrays!

The next example creates an array from user input and then compares elements from a 1D Array to the first column of a 2D array to return the matches as words.

; sample number string split and convert to words
; uncomment the ConsoleWrite and _ArrayDisplay lines to see what it does
;# some includes
#include <Array.au3>

;# some variables

;Create 2D array to display
Local $aWords[10][2] = [[0, 'nil'] _
		, [1, 'one'] _
		, [2, 'two'] _
		, [3, 'three'] _
		, [4, 'four'] _
		, [5, 'five'] _
		, [6, 'six'] _
		, [7, 'seven'] _
		, [8, 'eight'] _
		, [9, 'nine']] ; :)

;~ _ArrayDisplay($aWords)

; the order of the sample digits is to show that the comparison below is accurate, from left to right
Local $Originalinput = InputBox("Type a number", "A number?","97524601")

Local $userinput = $Originalinput ; pass the orginal input to a second var
; now parse the userinput to that we know what was submitted, then repeat in words
; first strip all white space
$userinput = StringStripWS($userinput, 8)

If StringIsDigit($userinput) Then ; continue

	; now get the length of the string
	Local $islong = StringLen($userinput)

	; make an array with size = length to insert items into
	Local $aInput[1] ; create blank one dimensional array
;~ 	_ArrayDisplay($aInput)
	; eat the string one digit at a time :)
	Local $oneleft
	For $i = 1 To $islong Step 1
		$oneleft = StringLeft($userinput, 1)
		ConsoleWrite("$oneleft: " & $oneleft & @CRLF)
		$userinput = StringTrimLeft($userinput, 1) ; make the $userinput one shorter on each loop
		_ArrayAdd($aInput, $oneleft, 1)
	Next
;~ 	_ArrayDisplay($aInput)
	; now write out what the user typed in words

;~ 	ConsoleWrite("How long is the string: " & $islong & @CRLF)
; compare the user input to our defined array
	For $k = 0 To $islong Step 1
		For $j = 0 To (UBound($aWords) - 1) Step 1
;~ 			ConsoleWrite("Compare $j " & $aWords[$j][0] & " to $k " & $aInput[$k] & @CRLF)
			If $aWords[$j][0] == $aInput[$k] Then
				MsgBox(0, "Number to word", $Originalinput&@CRLF&"User typed "& $aInput[$k]& @CRLF & $aWords[$j][1] & @CRLF,1)
			EndIf
;~ 			ConsoleWrite("For... Next inner loops: " & $k & @CRLF)
		Next
;~ 		ConsoleWrite("For... Next outer loops: " & $j & @CRLF)
	Next

EndIf ; if its not digits, skip

Multi Dimensional Arrays

Now what is a good explanation of a multi-dimensional array? It could be a table where you access one item in the table at a time.

Local  $arr[3][3] = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

For $i = 0 to UBound( $arr, 1) - 1
    For $j = 0 to UBound($arr, 2) - 1
        ConsoleWrite("$arr[" & $i & "][" & $j & "]:=" & $arr[$i][$j] & @LF)
    Next 

    ConsoleWrite(@LF)
Next

You can add a number of dimensions not to exceed sixty-four as stated in the help file section AutoIt3 limits/Defaults.

Here is a four dimensional example. You tell me how that initializer is for readability.

 
; NOTE: The following is supposed to be all on one line
; but we use the "_" character to split it into multiple lines for readability

Local $arr[3][3][3][3] = _
		[ _
		[ _
		[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
		[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
		[[1, 2, 3], [2, 3, 4], [3, 4, 5]] _
		], _
		[ _
		[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
		[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
		[[1, 2, 3], [2, 3, 4], [3, 4, 5]] _
		], _
		[ _
		[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
		[[1, 2, 3], [2, 3, 4], [3, 4, 5]], _
		[[1, 2, 3], [2, 3, 4], [3, 4, 5]] _
		] _
		]

For $i = 0 To UBound($arr, 1) - 1
	For $j = 0 To UBound($arr, 2) - 1
		For $k = 0 To UBound($arr, 3) - 1
			For $l = 0 To UBound($arr, 4) - 1
				ConsoleWrite("$arr[" & $i & "][" & $j & "][" & $k & "][" & $l & "]:=" & $arr[$i][$j][$k][$l] & @LF)
			Next
		Next
	Next
Next

ConsoleWrite(@LF)

Arrays in Arrays

You may save an array in an array element (item). Remember that there may be issues if you pass an array containing arrays into a function and the embedded array is changed inside that function. So, as a general rule, try not to embed arrays within arrays unless you absolutely need to and are prepared to do rigorous testing to make sure your code will always work as expected.

You can access the elements of these embedded arrays directly (note the additional () required):

Local $aContainerArray[2]
Local $aInternalArray_0[2] = ["Internal-0-0","Internal-0-1"]
Local $aInternalArray_1[2] = ["Internal-1-0","Internal-1-1"]
$aContainerArray[0] = $aInternalArray_0
$aContainerArray[1] = $aInternalArray_1

ConsoleWrite("1 element of InternalArray_0: " & ($aContainerArray[0])[1] & @CRLF)
ConsoleWrite("0 element of InternalArray_1: " & ($aContainerArray[1])[0] & @CRLF)

More Information

AutoIt features a large list of User Defined Functions (UDF), among which is a module supplying extra array functions. You can find a reference on those functions in AutoIt's Help file as the last main chapter named User Defined Functions Reference.

An associative array is an array where the keys (index numbers) are string instead of integer, as they are in AutoIt. See the Associative Arrays page for more information.