Jump to content

UDFs for Nested Arrays


blindwig
 Share

Recommended Posts

Ok, recently I discovered that AutoIt allows you to have nested arrays - that is, you can fit an entire array into a single element of another (parent) array. Why is that cool? Because instead of having to deal with arrays individually:

$MyArray1=SetupMyArray(1)
$MyArray2=SetupMyArray(2)
$MyArray3=SetupMyArray(3)
;...
$I = DetermineWhichArrayToWorkWith()
If $i = 1 then
    WorkWithArray($MyArray1)
ElseIf $i = 2 then
    WorkWithArray($MyArray2)
ElseIf $i = 3 then
    WorkWithArray($MyArray3)
EndIf

(And obviously the code gets bigger and messier with the more arrays you have to deal with)

You can create an array to contain your arrays:

Dim $ArrayParent[4]
For $i = 1 to 3
    $ArrayParent[$i] = SetUpMyArray($i)
Next
$I = DetermineWhichArrayToWorkWith()
WorkWithArray($ParentArray[$i])

The only problem is that you can't access the child array elements directly. For instance, say you have a 1-dimensional array inside another 1-dimensional array. you couldn't access this by $Array[1][2], because that's reserved for 2-dimensional arrays. In fact, AutoIt gives no native way to deal with a nested array. The only thing you can do is either copy the array out to a temp array and work with it there, or past the parent element to a function that expects an array for input.

Anyway, I writing a set of UDFs for working with nested arrays. Here's the first 4 functions I've whipped up:

_ArrayNGet(ByRef $Array, $Path)

_ArrayNSet(ByRef $Array, $Path, $Value)

_ArrayNUBound(ByRef $Array, $Path, $Dimension)

_ArrayNReDim(ByRef $Array, $Path, $Dimensions)

They should be pretty self-explainatory from their names. Here's the parameters:

$Array = the parent array

$Path = the path to follow down to find the element you want to deal with. The path is a string, which could look like this: '3;5,6;4' which represents the 4th element of a 1-dimensional array, inside the [5][6] element inside a 2-dimensional array, inside the 3rd element of a 1-dimensional array.

$Value is the value to set to the given element (which of course could be another array)

$Dimension is the dimension to return, same as in the built-in UBound() function

$Dimensions is a string like '1,2,3' Which tells the dimensions to Dim the array to.

Note: These functions have the limitation of only being able to deal with 1-6 dimensional arrays, but they can be easily adapted to handle more.

Another note: These functions, on failure, will return a string containing a message describing why they failed. So in your code you could do this to help troubleshoot:

$i = _ArrayNGet($MyArray, '3;2,5')
If @Error then MsgBox(0,'Error ' & @Error, $i)

MWR_ArrayN.au3

Edited by blindwig
Link to comment
Share on other sites

I found this out too about a month ago. I don't know if this will help with your UDF's, but Ubound() can be done on an element of an array that has an array in it.

Like this....

Dim $a1[2]
Dim $a2[5]
$a1[0] = "$a1[0]"
$a1[1] = $a2
For $i = 0 To 4
    $a2[$i] = $i
Next
$m = UBound($a1[1])
MsgBox(0,"",$m)
Edited by quick_sliver007

.

Link to comment
Share on other sites

Wow, I wish I had known about nested arrays...

I've been working on making a set of fuzzy logic functions, and the only way I could find to store all the data between functions was as a bunch of strings that I kept splitting to arrays and rejoining.

Too bad I was mostly done, but this seems as if it will make things so much easier that it'll be worth the rewrite.

Link to comment
Share on other sites

I found this out too about a month ago. I don't know if this will help with your UDF's, but Ubound() can be done on an element of an array that has an array in it.

Like this....

Dim $a1[2]
Dim $a2[5]
$a1[0] = "$a1[0]"
$a1[1] = $a2
For $i = 0 To 4
    $a2[$i] = $i
Next
$m = UBound($a1[1])
MsgBox(0,"",$m)

<{POST_SNAPBACK}>

Yes, I realize this. But the thing is that you can only pass an array 1 level deep.

For example:

Dim $a1[3], $a2[4], $a3[5]
$a2[2] = $a3
$a1[1] = $a2

Now, given the code above, there is no way to get the values or bounds or any other info from the array that is on the 2nd level ($a3). That's why I wrote my UDFs - the functions in there can walk an array "tree" to retreive (or set) data or bounds on a specific child array.

My functions walk the tree by recursively passing each child array by reference.

And just FYI, here's how to use my functions:

;to get the upper bound of $a3:
_ArrayNUBound($a1, '1;2');this travels the 1st element of $a1, then the 2nd element of $a2
;or
_ArrayNUBound($a1[1], '2')

;To get the value of $a3[3]:
$Val = _ArrayNGet($a1, '1;2;3')

;To create a 2-d 6x7 child array inside $a3[3]:
;method 1:
_ArrayNReDim($a1, '1;2;3', '6,7')
;method 2:
Dim $a4[6][7]
_ArrayNSet($a1, '1;2;3', $a4)
Link to comment
Share on other sites

I think AutoIt should be able to deal with nested arrays like this:

Nest $aArray[1[3[2[5]]]]
$aArray[0[2[0[3]]]] = "string"

...etc etc...

Also I have been working on versions of yours that support an infinite number of nested arrays (or as much as AutoIt can handle). Be warned:

These functions currently have no error-checking so if you mess up, you could get some strange results.

Here they are:

nested_array.au3

The syntax is pretty much the same as in blindwig's version. $nBound is the "path" on the nested array "tree". Example:

Dim $a[3], $b[3], $c[3]
$c = _ArrayFill($c, "string")
$b = _ArrayFill($b, $c)
$a = _ArrayFill($a, $b)
$d = _ArrayNGet($a, "0;1;2")
MsgBox(0, "$a[0[1[2]]] = 'string'", $d)

Variable $d wil be equal to "string" because the entire nested array tree is filled with nested arrays that lead to an array with all of it's values equal to "string".

I am currently working on a function to create a nested array tree made of blank strings for the "leafs" (here meaning the last values in the nested array that are an array but are filled with blank strings). Although I have run into a roadblock. the code has gotten so confusing that I can't figure it out, although I believe I need to store a nested array tree to an array as many times as the user specifies (or something like that).

All help is greatly appreciated! :)

confusing_func.au3

Edited by erifash
Link to comment
Share on other sites

Also I have been working on versions of yours that support an infinite number of nested arrays (or as much as AutoIt can handle). Be warned:

<{POST_SNAPBACK}>

Er, my routine do work on an (almost) infinate level of nesting. The only thing they're limited to is AutoIt's recurse limit, which I think is somewhere in the neighborhood of 384?

Two major problems I see in your code (before even running it):

You only support 1d arrays. I support up to 6d arrays, and the code is easily modifable to support more.

You make copies of your arrays as you traverse them, so the deeper they get, the more ridiculasly slow your routine will go.

Link to comment
Share on other sites

Yes, I see... Well, it works fine when I need to StringSplit a string in an array and am too lazy to store it to a seperate array. This way I can indirectly write the results of StringSplit to an array. Too bad of the limit too, that really hinders the script functionality. As for me using only 1-dimensional arrays

Dim $aArray[1]

instead of 6-dimensional arrays

Dim $aArray[1][1][1][1][1][1]

(code messed up on purpose) I don't know what you mean, but I do believe you meant yours does 6 levels in the nested array tree. Sw00t.

Link to comment
Share on other sites

Yes, I see... Well, it works fine when I need to StringSplit a string in an array and am too lazy to store it to a seperate array. This way I can indirectly write the results of StringSplit to an array. Too bad of the limit too, that really hinders the script functionality. As for me using only 1-dimensional arrays

Dim $aArray[1]

instead of 6-dimensional arrays

Dim $aArray[1][1][1][1][1][1]

(code messed up on purpose) I don't know what you mean, but I do believe you meant yours does 6 levels in the nested array tree. Sw00t.

<{POST_SNAPBACK}>

I really don't get what you're saying about limits and script functionality, and only 6 levels?

First, I can very easily write the results of a string split to an array, and then pull them out again:

Dim $aMaster[10], $aTemp, $vReturn
$aTemp = StringSplit('this is just a test', ' ')
$aMaster[1] = $aTemp
;or you can _ArrayNSet($aMaster, '1', $aTemp) instead of the previous line
$aTemp = StringSplit('hello again', ' ')
$aMaster[2] = $aTemp
$aTemp = StringSplit('the quick brown fox...', ' ')
$aMaster[3] = $aTemp

;the third word of the first string:
$vReturn = _ArrayNGet($aMaster, '1;3')
MsgBox(0, @Error, $vReturn)
;the second word of the second string:
$vReturn = _ArrayNGet($aMaster, '2;2')
MsgBox(0, @Error, $vReturn)
;the third word of the third string:
$vReturn = _ArrayNGet($aMaster, '3;3')
MsgBox(0, @Error, $vReturn)

secondly, I can do multi-dimensional arrays. The functions are currently limited to 6-dimensional arrays, but can be easily expanded - just a matter of expanding the code in the select case. Here's multi-dimensional functionality:

;example of a 2d array inside an element of a 1d array:
Dim $aMaster[5], $aTemp[3][4]
$aTemp[1][2] = 'X marks the spot'
$aMaster[3] = $aTemp

$vReturn = _ArrayNGet($aMaster, '3;1,2')
MsgBox(0, @Error, $vReturn)

And finally, you say that my functions are limited to 6 levels? I stated previously that my functions are recursive, so they are only limited to AutoIt's recursion limits.

Here's an example of an array 9 levels deep:

Dim $aTemp[10]
$aTemp[1] = 'X marks the spot'
$aTemp[1] = $aTemp
$aTemp[1] = $aTemp
$aTemp[1] = $aTemp
$aTemp[1] = $aTemp
$aTemp[1] = $aTemp
$aTemp[1] = $aTemp
$aTemp[1] = $aTemp
$aTemp[1] = $aTemp
$vReturn = _ArrayNGet($aTemp, '1;1;1;1;1;1;1;1;1')
MsgBox(0, @Error, $vReturn)
Link to comment
Share on other sites

OK, I wrote a function to show the mapping of an array (including any nested arrays). This is very useful if you confuse yourself on what your code is doing with nested arrays - this gives you a good spot-check.

Here it is:

CODE

;Returns an array of UBound() results for $Array, where [0]=UBound($Array,0), [1]=UBound($Array,1), etc...

Func _ArrayGetDimensions(ByRef $Array)

Local $DimCount = UBound($Array, 0), $i, $dims[$DimCount + 1]

$dims[0] = $DimCount

For $i = 1 to $DimCount

$Dims[$i] = UBound($array, $i)

Next

Return $Dims

EndFunc

;Returns a string representation of the map of $Array, including nested arrays.

;supports arrays of up to 6 dimensions, and no harad limits on nesting levels

Func _ArrayNMap(ByRef $Array, $Depth = 0, $Prefix='[root]')

Local $Dimensions = _ArrayGetDimensions($Array), $i, $sLine = _StringRepeat(@TAB, $Depth) & $Prefix & '=', $sOut = ''

If $Dimensions[0] > 0 Then

$sLine = $sLine & 'Array'

For $i = 1 To $Dimensions[0]

$sLine = $sLine & '[' & UBound($Array, $i) & ']'

Next

$sOut = $sOut & $sLine & @CRLF

EndIf

Select

Case $Dimensions[0] = 0

Select

Case IsInt($Array)

$sLine = $sLine & 'Integer(' & $Array & ')'

Case IsFloat($Array)

$sLine = $sLine & 'Float(' & $Array & ')'

Case IsString($Array)

$sLine = $sLine & 'String[' & StringLen($Array) & ']'

Case Else

$sLine = $sLine & 'String[' & StringLen($Array) & ']'

EndSelect

$sOut = $sOut & $sLine & @CRLF

Case $Dimensions[0] = 1

For $i = 0 To $Dimensions[1] - 1

$sOut = $sOut & _ArrayNMap($Array[$i], $Depth + 1, '[' & $i & ']')

Next

Case $Dimensions[0] = 2

Local $j

For $i = 0 To $Dimensions[1] - 1

For $j = 0 To $Dimensions[2] - 1

$sOut = $sOut & _ArrayNMap($Array[$i][$j], $Depth + 1, '[' & $i & ']' & '[' & $j & ']')

Next

Next

Case $Dimensions[0] = 3

Local $j, $k

For $i = 0 To $Dimensions[1] - 1

For $j = 0 To $Dimensions[2] - 1

For $k = 0 To $Dimensions[3] - 1

$sOut = $sOut & _ArrayNMap($Array[$i][$j][$k], $Depth + 1, '[' & $i & ']' & '[' & $j & ']' & '[' & $k & ']')

Next

Next

Next

Case $Dimensions[0] = 4

Local $j, $k, $l

For $i = 0 To $Dimensions[1] - 1

For $j = 0 To $Dimensions[2] - 1

For $k = 0 To $Dimensions[3] - 1

For $l = 0 To $Dimensions[4] - 1

$sOut = $sOut & _ArrayNMap($Array[$i][$j][$k][$l], $Depth + 1, '[' & $i & ']' & '[' & $j & ']' & '[' & $k & ']' & '[' & $l & ']')

Next

Next

Next

Next

Case $Dimensions[0] = 5

Local $j, $k, $l, $m

For $i = 0 To $Dimensions[1] - 1

For $j = 0 To $Dimensions[2] - 1

For $k = 0 To $Dimensions[3] - 1

For $l = 0 To $Dimensions[4] - 1

For $m = 0 To $Dimensions[5] - 1

$sOut = $sOut & _ArrayNMap($Array[$i][$j][$k][$l][$m], $Depth + 1, '[' & $i & ']' & '[' & $j & ']' & '[' & $k & ']' & '[' & $l & ']' & '[' & $m & ']')

Next

Next

Next

Next

Next

Case $Dimensions[0] = 6

Local $j, $k, $l, $m, $n

For $i = 0 To $Dimensions[1] - 1

For $j = 0 To $Dimensions[2] - 1

For $k = 0 To $Dimensions[3] - 1

For $l = 0 To $Dimensions[4] - 1

For $m = 0 To $Dimensions[5] - 1

For $n = 0 To $Dimensions[6] - 1

$sOut = $sOut & _ArrayNMap($Array[$i][$j][$k][$l][$m][$n], $Depth + 1, '[' & $i & ']' & '[' & $j & ']' & '[' & $k & ']' & '[' & $l & ']' & '[' & $m & ']' & '[' & $n & ']')

Next

Next

Next

Next

Next

Next

EndSelect

Return $sOut

EndFunc

And using it is simple:

MsgBox(0, 'Array Map', _ArrayNMap($MyArray))

Edit:Fixed bug in displaying multi-dimensional arrays

Edited by blindwig
Link to comment
Share on other sites

Leave it on good old paint to explain things...

Here is what I mean:

As for what you meant I understand it can do many dimensions, but to do more than six levels you need to manually edit the case statements inside the function. Mine supports as many levels as AutoIt can handle in addition to as many dimensions as AutoIt can handle. But NOT two-dimensional arrays ($aArray[1][2]).

Edited by erifash
Link to comment
Share on other sites

Leave it on good old paint to explain things...

Here is what I mean:

As for what you meant I understand it can do many dimensions, but to do more than six levels you need to manually edit the case statements inside the function. Mine supports as many levels as AutoIt can handle in addition to as many dimensions as AutoIt can handle. But NOT two-dimensional arrays ($aArray[1][2]).

<{POST_SNAPBACK}>

I think we're having a clash of terminology here...

First of all, your image demonstrates a 2-dimensional array, not a nested array.

Secondly, what you are calling "dimensions" are really just indexes along a single dimension. And what you are calling "levels" are just indexes down a 2nd dimension.

Third, you say your function supports as many dimensions as AutoIt can handle, but not 2 or more. So what you're really saying is that I can have as many dimensions as I want, as long as I only want 1 :) You ever been to a car dealship and they tell you that you can have that car in any color you want, as long as you want it in red?

I don't want to re-type all the stuff I said a few posts back, but it still stands that:

My function can handle unlimited levels of array nesting. I use function recursion, so I am only limited by AutoIt recursion limits. Look at the example - you said I was limited to 6 levels, but I showed you that I can do at least 9.

Also, I pass each array ByRef instead of copying it (like yours does) so I will not hit any memory limits or resource slowdowns.

My function can also handle multi-dimensional arrays (currently supports up to 6, adaptable for more) while yours only supports a single dimension. Look at my example, I showed you my function handling 2 and 3 dimensional arrays.

Finally, if you don't really understand array nesting, look at this:

Dim $aMaster[5], $a1[3][3], $a2[6]
$a1[0][1] = $a2
Dim $a2[8]
$a1[1][2] = $a2
$aMaster[2] = $a1
Dim $a1[6]
$aMaster[3] = $a1
Dim $a1[2][2][2]
$aMaster[4] = $a1
msgbox(0,'',_ArrayNMap($aMaster))

Which will show you this, a true diagram of nested arrays - multi-level and multi-dimension:

[root]=Array[5]
    [0]=Integer(0)
    [1]=Integer(0)
    [2]=Array[3][3]
        [0][0]=Integer(0)
        [0][1]=Array[6]
            [0]=Integer(0)
            [1]=Integer(0)
            [2]=Integer(0)
            [3]=Integer(0)
            [4]=Integer(0)
            [5]=Integer(0)
        [0][2]=Integer(0)
        [1][0]=Integer(0)
        [1][1]=Integer(0)
        [1][2]=Array[8]
            [0]=Integer(0)
            [1]=Integer(0)
            [2]=Integer(0)
            [3]=Integer(0)
            [4]=Integer(0)
            [5]=Integer(0)
            [6]=Integer(0)
            [7]=Integer(0)
        [2][0]=Integer(0)
        [2][1]=Integer(0)
        [2][2]=Integer(0)
    [3]=Array[6]
        [0]=Integer(0)
        [1]=Integer(0)
        [2]=Integer(0)
        [3]=Integer(0)
        [4]=Integer(0)
        [5]=Integer(0)
    [4]=Array[2][2][2]
        [0][0][0]=Integer(0)
        [0][0][1]=Integer(0)
        [0][1][0]=Integer(0)
        [0][1][1]=Integer(0)
        [1][0][0]=Integer(0)
        [1][0][1]=Integer(0)
        [1][1][0]=Integer(0)
        [1][1][1]=Integer(0)
Link to comment
Share on other sites

Fixed a bug in _ArrayNMap(), it now iterates multi-dimensional array properly. Before it was using the UBound of the first dimension for all dimensions. Now it takes the UBound of each dimension properly. The new code was edited into the original post.

Link to comment
Share on other sites

  • 2 years later...

Runnning this (even your test ones) returns me only 0's for my code. Even using your test of

Dim $aMaster[10], $aTemp, $vReturn
$aTemp = StringSplit('this is just a test', ' ')
$aMaster[1] = $aTemp
;or you can _ArrayNSet($aMaster, '1', $aTemp) instead of the previous line
$aTemp = StringSplit('hello again', ' ')
$aMaster[2] = $aTemp
$aTemp = StringSplit('the quick brown fox...', ' ')
$aMaster[3] = $aTemp

;the third word of the first string:
$vReturn = _ArrayNGet($aMaster, '1;3')
MsgBox(0, @Error, $vReturn)
;the second word of the second string:
$vReturn = _ArrayNGet($aMaster, '2;2')
MsgBox(0, @Error, $vReturn)
;the third word of the third string:
$vReturn = _ArrayNGet($aMaster, '3;3')
MsgBox(0, @Error, $vReturn)

Returns nothing but 0's for the test boxes.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...