Jump to content

This site uses cookies. By continuing to browse the site you are agreeing to our use of cookies. Find out more here. X
X


Photo

UDFs for Nested Arrays


  • Please log in to reply
13 replies to this topic

#1 blindwig

blindwig

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 772 posts

Posted 27 June 2005 - 01:51 AM

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)

Attached Files


Edited by blindwig, 27 June 2005 - 01:51 AM.








#2 quick_sliver007

quick_sliver007

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 334 posts

Posted 28 June 2005 - 07:34 AM

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, 28 June 2005 - 07:35 AM.

.

#3 mother9987

mother9987

    Seeker

  • Active Members
  • 47 posts

Posted 28 June 2005 - 02:39 PM

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.

#4 blindwig

blindwig

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 772 posts

Posted 28 June 2005 - 03:13 PM

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)


#5 erifash

erifash

    autoit - think free

  • Active Members
  • PipPipPipPipPipPip
  • 517 posts

Posted 03 July 2005 - 05:38 PM

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:

Attached File  nested_array.au3   640bytes   369 downloads

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! :)

Attached File  confusing_func.au3   704bytes   319 downloads

Edited by erifash, 03 July 2005 - 05:40 PM.


#6 blindwig

blindwig

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 772 posts

Posted 04 July 2005 - 08:27 AM

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.

#7 erifash

erifash

    autoit - think free

  • Active Members
  • PipPipPipPipPipPip
  • 517 posts

Posted 05 July 2005 - 03:08 AM

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.

#8 busysignal

busysignal

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 349 posts

Posted 05 July 2005 - 07:17 AM

blindwig, nice array funcitons.. It is nice to have a flexable array structure..

Two-Thumbs up!!! :evil:

Cheers.. :)

#9 blindwig

blindwig

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 772 posts

Posted 05 July 2005 - 08:31 AM

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)


#10 blindwig

blindwig

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 772 posts

Posted 05 July 2005 - 07:13 PM

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, 06 July 2005 - 04:45 PM.


#11 erifash

erifash

    autoit - think free

  • Active Members
  • PipPipPipPipPipPip
  • 517 posts

Posted 05 July 2005 - 09:53 PM

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, 05 July 2005 - 09:55 PM.


#12 blindwig

blindwig

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 772 posts

Posted 06 July 2005 - 04:44 PM

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:
Plain Text         
[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)


#13 blindwig

blindwig

    Universalist

  • Active Members
  • PipPipPipPipPipPip
  • 772 posts

Posted 06 July 2005 - 04:48 PM

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.

#14 Alodar

Alodar

    Wayfarer

  • Active Members
  • Pip
  • 52 posts

Posted 14 July 2007 - 01:02 AM

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.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users