OHB Posted April 17, 2010 Posted April 17, 2010 (edited) Hi all! I've spent a lot of time reading these forums and I have learned MUCH. So, thanks for that! Anyway, I decided I wanted multidimensional associative arrays for use in my scripts...and I couldn't find anything I liked. So, I wrote one! Here's the code: expandcollapse popup#include-once ; #INDEX# ======================================================================================================================= ; Title .........: xHashCollection ; AutoIt Version : 3.3.4.0 ; Language ......: English ; Description ...: Create and use Multidimentional Associative Arrays ; Author ........: OHB <me at orangehairedboy dot com> ; =============================================================================================================================== Global $_xHashCollection = ObjCreate( "Scripting.Dictionary" ), $_xHashCache ; #FUNCTION# ==================================================================================================================== ; Name...........: x ; Description ...: Gets or sets a value in an Associative Array ; Syntax.........: SET: x( $sKey , $vValue ) ; GET: x( $key ) ; Parameters ....: $sKey - the key to set or get. Examples: ; x( 'foo' ) gets value of foo ; x( 'foo.bar' ) gets value of bar which is a key of foo ; $bar = "baz" ; x( 'foo.$bar' ) gets value of baz which is a key of foo (variables are expanded) ; Return values .: Success - When setting, return the value set. When getting, returns the requested value. ; Failure - Returns a 0 ; Author ........: OHB <me at orangehairedboy dot com> ; =============================================================================================================================== Func x( $sKey = '' , $vValue = '' ) $func = "get" If @NumParams <> 1 Then $func = "set" If $sKey == '' Then If $func == "get" Then Return $_xHashCollection Else $_xHashCollection.removeAll Return '' EndIf EndIf $parts = StringSplit( $sKey , "." ) $last_key = $parts[$parts[0]] $cur = $_xHashCollection For $x = 1 To $parts[0] - 1 If Not $cur.exists( $parts[$x] ) Then If $func == "get" Then Return $cur.add( $parts[$x] , ObjCreate( "Scripting.Dictionary" ) ) EndIf $cur = $cur.item( $parts[$x] ) Next If IsPtr( $vValue ) Then $vValue = String( $vValue ) If $func == "get" Then If Not $cur.exists( $last_key ) Then Return $item = $cur.item( $last_key ) Return $item ElseIf Not $cur.exists( $last_key ) Then $cur.add( $last_key , $vValue ) Else $cur.item( $last_key ) = $vValue EndIf Return $vValue EndFunc ; #FUNCTION# ==================================================================================================================== ; Name...........: x_unset ; Description ...: Removes a key from an Associative Array ; Syntax.........: x_unset( $sKey ) ; Parameters ....: $sKey - the key to remove. ; Return values .: Success - True ; Failure - False ; Author ........: OHB <me at orangehairedboy dot com> ; =============================================================================================================================== Func x_unset( $sKey ) If $sKey == '' Then Return x( '' , '' ) $parts = StringSplit( $sKey , "." ) $cur = $_xHashCollection For $x = 1 To $parts[0] - 1 If Not $cur.exists( $parts[$x] ) Then Return False $cur = $cur.item( $parts[$x] ) Next $cur.remove( $parts[$parts[0]] ) Return True EndFunc ; #FUNCTION# ==================================================================================================================== ; Name...........: x_display ; Description ...: Displays the contents of an Associative Array ; Syntax.........: x_display( $sKey ) ; Parameters ....: $sKey - the key to display. Examples: ; x_display() displays everything ; x_display( 'foo' ) displays the contents of foo ; Author ........: OHB <me at orangehairedboy dot com> ; =============================================================================================================================== Func x_display( $key = '' ) $text = $key If $key <> '' Then $text &= " " $text &= StringTrimRight( _x_display( x( $key ) , '' ) , 2 ) $wHnd = GUICreate( "Array " & $key , 700 , 500 ) GUISetState( @SW_SHOW , $wHnd ) $block = GUICtrlCreateEdit( $text , 5 , 5 , 690 , 490 ) GUICtrlSetFont( $block , 10 , 400 , -1 , 'Courier' ) While 1 If GUIGetMsg() == -3 Then ExitLoop WEnd GUISetState( @SW_HIDE , $wHnd ) GUIDelete( $wHnd ) EndFunc ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: _x_display ; Description ...: Itterates through an array and builds output for x_display ; Author ........: OHB <me at orangehairedboy dot com> ; =============================================================================================================================== Func _x_display( $item , $tab ) If IsObj( $item ) Then $text = 'Array (' & @CRLF $itemAdded = False For $i In $item $text &= $tab & " [" & $i & "] => " & _x_display( $item.item($i) , $tab & " " ) $itemAdded = True Next If Not $itemAdded Then $text &= @CRLF $text &= $tab & ')' ElseIf IsArray( $item ) Then $text = "Array" $totalItems = 1 $dimensions = UBound( $item , 0 ) For $dimension = 1 To $dimensions $size = UBound( $item , $dimension ) $totalItems *= $size $text &= "[" & $size & "]" Next $text &= " (" & @CRLF For $itemID = 0 To $totalItems - 1 $idName = '' $idNum = $itemID For $dimension = 1 To $dimensions - 1 $mul = ( $totalItems / UBound( $item , $dimension ) ) $a = Floor( $idNum / $mul ) $idName &= '[' & $a & ']' $idNum -= ( $a * $mul ) Next $idName &= '[' & $idNum & ']' $text &= $tab & " " & $idName & " => " & _x_display( Execute( "$item" & $idName ) , $tab & " " ) Next $text &= $tab & ")" Else $text = $item EndIf $text &= @CRLF Return $text EndFunc And here's how to use it: x( 'foo' , 'bar' ) ; foo = bar x( 'bar.foo' , 'baz' ) ; bar[foo] = baz MsgBox( 0 , x( 'foo' ) , x( 'bar.foo' ) ); outputs 'bar' and 'baz' The x function (chosen so it has a nice short name) takes two parameters: $key, $value. If the value is supplied, the value is set, otherwise it is retrieved. Create multi-dimensional arrays by using dot-notation. x( 'family.father.name' , 'Paul' ) is equivalent to the following PHP command: $family['father']['name'] = 'Paul'; Retrieve the value the same way! msgbox( 0 , 'Family' , 'My father's name is' & x( 'family.father.name' ) ) And, of course, you can use dynamic names using variables (provided the values don't contain dots). x( "localization.en.lang_en","English") x( "localization.es.lang_en","Ingles") For $language_code In x( 'localization' ) MsgBox( 0 , 'Language' , x( 'localization.'&$language_code&'.lang_en' ) ) Next Setting a value returns the set value, so you can do things like: x( 'foo' , x( 'bar' , 'baz' ) ) ;foo and bar both contain baz If ( x( 'query' , $query ) == '' ) MsgBox( 0 , 'Error' , 'Query cannot be blank!' ) You can use x_display to have a look at the structure. x( 'foo' , x( 'bar' , 'baz' ) ) x( 'yada' , 'yada' ) x_display() ;display everything x_display( 'foo' ) ;display just foo TODO: * Serialization / Unserialization I would appreciate feedback and suggestions! Enjoy Edited April 21, 2010 by OHB qsek 1
picea892 Posted April 17, 2010 Posted April 17, 2010 (edited) I like this very much. Very nice first post, hope you stick around here for a bit. One small error checking suggestion. In your version the code crashes if I do this x( 'beef' , 'Meat' ) x( 'poultry' , 'Meat' ) x( 'beef' , 'Fruit' ) MsgBox( 0 , x( 'poultry' ) , x( 'beef' ) ) To solve this, you can add this: if @error then Return just before this line $cur.add( $last_key , $value Edited April 17, 2010 by picea892
OHB Posted April 18, 2010 Author Posted April 18, 2010 (edited) I like this very much. Very nice first post, hope you stick around here for a bit. One small error checking suggestion. In your version the code crashes if I do this x( 'beef' , 'Meat' ) x( 'poultry' , 'Meat' ) x( 'beef' , 'Fruit' ) MsgBox( 0 , x( 'poultry' ) , x( 'beef' ) ) Edit: Code corrected in first post. Edited April 18, 2010 by OHB
picea892 Posted April 18, 2010 Posted April 18, 2010 Not sure what the problem is with the edit button, should be bottom right. I have reported your post (bottom left) and asked them to move it to the examples forum.
picea892 Posted April 18, 2010 Posted April 18, 2010 Hi againNot my best work, but am out of time. Here is where I think you could go with this....expandcollapse popup#include <GUIConstantsEx.au3> #include <Array.au3> #include-once Global $_xHashCollection = ObjCreate( "Scripting.Dictionary" ) dim $array[10][2]=[["Pine","Conifer"],["Spruce","Conifer"],["Larch","Conifer"],["Fir","Conifer"],["Poplar","Deciduous"],["Birch","Deciduous"],["Maple","Deciduous"],["Oak","Deciduous"],["Ash","Deciduous"],["Basswood","Deciduous"]] _arraytox($array) _ArrayDisplay(_xvalue("Conifer")) func _arraytox($arrayed) $rows = UBound($arrayed) $cols = UBound($arrayed, 2) for $i = 0 to $rows-1 $ongoing="" for $j=0 to $cols-1 $ongoing=$ongoing&$arrayed[$i][$j] if $j<>$cols-1 then $ongoing=$ongoing&"," Next x($ongoing,"x") ;the "x" is just a placeholder to trigger @NumParams in the x function Next EndFunc func _xvalue($value) $sItem="" $count=0 For $vKey In $_xHashCollection if $_xHashCollection.Item ($vKey)=$value Then if $count=0 then $sItem= $vKey if $count<>0 then $sItem= $sItem&","&$vKey $count=$count+1 EndIf Next $temparray=StringSplit($sItem,",") Return $temparray EndFunc ; #FUNCTION# ==================================================================================================================== ; Name...........: x ; Description ...: Gets or sets a value in an Associative Array ; Syntax.........: SET: x( $sKey , $vValue ) ; GET: x( $key ) ; Parameters ....: $sKey - the key to set or get. Examples: ; x( 'foo' ) gets value of foo ; x( 'foo.bar' ) gets value of bar which is a key of foo ; $bar = "baz" ; x( 'foo.$bar' ) gets value of baz which is a key of foo (variables are expanded) ; Return values .: Success - When setting, return the value set. When getting, returns the requested value. ; Failure - Returns a 0 ; Author ........: OHB <me at orangehairedboy dot com> ; =============================================================================================================================== Func x( $sKey = '' , $vValue = '' ) if StringInStr($sKey,",") then $temparray=StringSplit($sKey,",") $sKey=$temparray[1] $vValue=$temparray[2] EndIf $func = "get" If @NumParams == 2 Then $func = "set" If $sKey == '' Then If $func == "get" Then Return $_xHashCollection Else $x_HashCollection = ObjCreate( "Scripting.Dictionary" ) Return '' EndIf EndIf $parts = StringSplit( $sKey , "." ) $cur = $_xHashCollection For $x = 1 To $parts[0] - 1 If StringLeft( $parts[$x] , 1 ) == "$" Then $parts[$x] = Eval( $parts[$x] ) If Not $cur.exists( $parts[$x] ) Then If $func == "get" Then Return $cur.add( $parts[$x] , ObjCreate( "Scripting.Dictionary" ) ) EndIf $cur = $cur.item( $parts[$x] ) Next $last_key = $parts[$parts[0]] If $func == "get" Then If Not $cur.exists( $last_key ) Then Return Return $cur.item( $last_key ) ElseIf Not $cur.exists( $last_key ) Then $cur.add( $last_key , $vValue ) Else $cur.item( $last_key ) = $vValue EndIf Return $vValue EndFunc ; #FUNCTION# ==================================================================================================================== ; Name...........: x_display ; Description ...: Displays the contents of an Associative Array ; Syntax.........: x_display( $sKey ) ; Parameters ....: $sKey - the key to display. Examples: ; x_display() displays everything ; x_display( 'foo' ) displays the contents of foo ; Author ........: OHB <me at orangehairedboy dot com> ; =============================================================================================================================== Func x_display( $key = '' ) $text = $key If $key <> '' Then $text &= " " $text &= _x_display( x( $key ) , '' ) $wHnd = GUICreate( "Array " & $key , 700 , 500 ) GUISetState( @SW_SHOW , $wHnd ) $block = GUICtrlCreateEdit( $text , 5 , 5 , 690 , 490 ) GUICtrlSetFont( $block , 10 , 400 , -1 , 'Courier' ) While 1 If GUIGetMsg() == $GUI_EVENT_CLOSE Then ExitLoop WEnd GUISetState( @SW_HIDE , $wHnd ) GUIDelete( $wHnd ) EndFunc ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: _x_display ; Description ...: Itterates through an array and builds output for x_display ; Author ........: OHB <me at orangehairedboy dot com> ; =============================================================================================================================== Func _x_display( $item , $tab ) If IsObj( $item ) Then $text = '[' & @CRLF For $i In $item $text &= $tab & " " & $i & " " & _x_display( $item.item($i) , $tab & " " ) Next $text &= $tab & ']' Else $text = "=> '" & $item & "'" EndIf $text &= @CRLF Return $text EndFuncAlso see this post
OHB Posted April 19, 2010 Author Posted April 19, 2010 Hi again Not my best work, but am out of time. Here is where I think you could go with this.... I'm not sure where you're trying to go with that. Best I can tell is that perhaps you didn't understand the purpose of the functions. The point is to get rid of regular arrays altogether...at least when an associative array will work better. In addition, the ability to store data in a multi-dimensional structure is quite valuable. From what I can tell, your script was an example for how to store a list of trees and the type of tree it is. Here's how you should do it: x( 'Tree.Pine.type' , 'Conifer' ) x( 'Tree.Spruce.type' , 'Conifer' ) x( 'Tree.Larch.type' , 'Conifer' ) x( 'Tree.Fir.type' , 'Conifer' ) x( 'Tree.Popular.type' , 'Deciduous' ) x( 'Tree.Birch.type' , 'Deciduous' ) x( 'Tree.Maple.type' , 'Deciduous' ) x( 'Tree.Oak.type' , 'Deciduous' ) x( 'Tree.Ash.type' , 'Deciduous' ) x( 'Tree.Basswood.type' , 'Deciduous' ) x( 'Tree.Pine.type' , 'Deciduous' ) x_display() For $tree in x( 'Tree' ) ConsoleWrite( $tree & " tree is " & x( 'Tree.'&$tree&'.type' ) & @CRLF ) Next Check out the x_display() popup to see how the data is organized. Using this method requires you to give some thought to what data you want to store, how you want to store it, all in relation to how you will use it and how you need to get the data. But once you get over that little hurdle, you'll find such a mechanism invaluable.
picea892 Posted April 19, 2010 Posted April 19, 2010 Thanks OHB, that add clarity. I may be missing the point and be stuck in the normal array methods. I put forward those methods because many of the functions I commonly use in autoit for data manipulation either require an array or produce an array in the standard format so it is advantageous to easily convert. Some examples Inireadsection; inireadsectionnames; stringsplit; _ExcelReadArray; _ExcelReadSheetToArray I am also struggling with having to know the tree name before I can query it's type. Not belittling your work, just trying to learn another data manipulation technique. Cheers. Picea892
OHB Posted April 19, 2010 Author Posted April 19, 2010 I put forward those methods because many of the functions I commonly use in autoit for data manipulation either require an array or produce an array in the standard format so it is advantageous to easily convert. Some examples Inireadsection; inireadsectionnames; stringsplit; _ExcelReadArray; _ExcelReadSheetToArray And that's great...I will hopefully be adding additional functionality down the line to provide some interoperability. I am also struggling with having to know the tree name before I can query it's type. Think of it this way: You can't open a file unless you first know where the file is. So, you need to know the folder before you can open the file. It's the same principle. The file belongs to that folder. In that same way, "type" is a property of "tree" and therefore belongs to "tree". Using associative arrays isn't necessarily always the best thing...it depends on the data you're storing. If you need to store objects with properties, then this is the way to go. I used this to provide an easy way to localize an application that I'm working on. Here's an example: x( "localization.en.lang_name","English") x( "localization.en.i_speak","I speak English") x( "localization.fr.lang_name","Français") x( "localization.fr.i_speak","Je parle français") x( "localization.es.lang_name","Español") x( "localization.es.i_speak","Hablo español") ; start with English Global $currentLanguage = "en" MsgBox( 0 , getLocalized( 'lang_name' ) , getLocalized( 'i_speak' ) ) ; change the language to French $currentLanguage = "fr" MsgBox( 0 , getLocalized( 'lang_name' ) , getLocalized( 'i_speak' ) ) ; change the language to Spanish $currentLanguage = "es" MsgBox( 0 , getLocalized( 'lang_name' ) , getLocalized( 'i_speak' ) ) ; let's look at the structure of the stored data x_display() Func getLocalized( $label ) Return x( 'localization.'&$currentLanguage&'.'&$label ) EndFunc My localization file is about 800 lines and has content for 14 languages. Adding a new language is as simple as adding new lines to the localization file - the program adapts automatically. I also don't have to mess with defining arrays, numbers, or UBound. Just makes life simpler!
dmob Posted April 19, 2010 Posted April 19, 2010 Very nice!! I've learnt something, and I'm going to use this in two of my projects.
Kip Posted April 19, 2010 Posted April 19, 2010 I'm not sure "x" is the right name for the function. It's too short, doesn't describe anything and the chance is very high someone wants to create their own function called that way. MailSpons: Fake SMTP server for safe email testing Dutch postcode & address API.
OHB Posted April 19, 2010 Author Posted April 19, 2010 I'm not sure "x" is the right name for the function. It's too short, doesn't describe anything and the chance is very high someone wants to create their own function called that way.Well, rename it if you want. Or suggest a new name. As I explained in my first post, I wanted to name that's short and simple. I'd also submit that if someone wants to create their own "x" function...they should also choose a better name.
shEiD Posted April 20, 2010 Posted April 20, 2010 This is probably the most useful thing I've found in the forums from the time I started using autoit. Thank you, thank you, thank you. A single function, and only 31 lines at that? Jesus I'm sorry, but I really don't get it, why this is not built-in into autoit by default? PS. Such a small snippet, but would have saved me days, or even weeks of misery, now that I think about it. @OHB
wraithdu Posted April 20, 2010 Posted April 20, 2010 Perhaps a typo - Else $x_HashCollection = ObjCreate( "Scripting.Dictionary" ) Return '' EndIf Shouldn't the var be $_xHashCollection? Otherwise that Else block doesn't do anything except return a blank string... (keep in mind I didn't do any in depth analysis here yet...)
OHB Posted April 21, 2010 Author Posted April 21, 2010 (edited) This is probably the most useful thing I've found in the forums from the time I started using autoit. Thank you, thank you, thank you. A single function, and only 31 lines at that? Jesus I'm sorry, but I really don't get it, why this is not built-in into autoit by default? PS. Such a small snippet, but would have saved me days, or even weeks of misery, now that I think about it. @OHB Glad you like it! x_display() has been updated so you can new view the contents of regular arrays that are stored using this method. Try: Local $myArray[2][2] = [[1,2],[3,4]] x( 'some.key' , $myArray ) x_display() Perhaps a typo - Else $x_HashCollection = ObjCreate( "Scripting.Dictionary" ) Return '' EndIf Shouldn't the var be $_xHashCollection? Otherwise that Else block doesn't do anything except return a blank string... (keep in mind I didn't do any in depth analysis here yet...) Typo fixed and line changed to utilize removeAll instead. Thanks! Edited April 21, 2010 by OHB
shEiD Posted April 21, 2010 Posted April 21, 2010 After update: C:\xHashCollection.au3(37,70) : WARNING: $_xHashCache: possibly used before declaration. If IsDeclared("_xHashCache") And $_xHashCache.exists($all_but_last) Then ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ C:\xHashCollection.au3(37,70) : ERROR: $_xHashCache: undeclared global variable. If IsDeclared("_xHashCache") And $_xHashCache.exists($all_but_last) Then ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ C:\test.au3 - 1 error(s), 1 warning(s) !>21:00:14 AU3Check ended.rc:2 >Exit code: 0 Time: 2.240
OHB Posted April 21, 2010 Author Posted April 21, 2010 (edited) After update: C:\xHashCollection.au3(37,70) : WARNING: $_xHashCache: possibly used before declaration. If IsDeclared("_xHashCache") And $_xHashCache.exists($all_but_last) Then ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ C:\xHashCollection.au3(37,70) : ERROR: $_xHashCache: undeclared global variable. If IsDeclared("_xHashCache") And $_xHashCache.exists($all_but_last) Then ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ C:\test.au3 - 1 error(s), 1 warning(s) !>21:00:14 AU3Check ended.rc:2 >Exit code: 0 Time: 2.240 That's an issue with AU3Chcek - it isn't smart enough to know that there isn't actually an error. To get around that, I've modified the code so it doesn't use Assign. Edited April 21, 2010 by OHB
shEiD Posted April 21, 2010 Posted April 21, 2010 Typo fixed and line changed to utilize removeAll instead. Thanks! I was about to suggest you changed $GUI_EVENT_CLOSE to -3 and ask about clearing the object Would it be possible to clear only a part of the array? ie: tree.key1 tree.key2 tree.key3 tree.key2.something tree.key2.somethingelse Would be good to clear tree.key2, so it leaves: tree.key1 tree.key2 tree.key3 Or maybe it would leave only: tree.key1 tree.key3 I don't know how these things work, its completely new to me, but I LOVE it. And thanks again.
shEiD Posted April 21, 2010 Posted April 21, 2010 (edited) That's an issue with AU3Chcek - it isn't smart enough to know that there isn't actually an error. To get around that, I've modified the code so it doesn't use Assign.Yep, they work, it's just scite is whining all the time.Edit: thanks for modifying that. ...I can't keep up with the updates Edited April 21, 2010 by shEiD
OHB Posted April 21, 2010 Author Posted April 21, 2010 I was about to suggest you changed $GUI_EVENT_CLOSE to -3 and ask about clearing the object Would it be possible to clear only a part of the array? ie: tree.key1 tree.key2 tree.key3 tree.key2.something tree.key2.somethingelse Would be good to clear tree.key2, so it leaves: tree.key1 tree.key2 tree.key3 Or maybe it would leave only: tree.key1 tree.key3 I don't know how these things work, its completely new to me, but I LOVE it. And thanks again. Right now you can do x( 'tree.key2' , '' ) and the contents of key2 would be erased, but the key would still be there. I'll be adding an unset function soon
OHB Posted April 21, 2010 Author Posted April 21, 2010 (edited) Note: I've just removed the caching function because of an unforeseen error. On the plus-side, there's now an x_unset() function. x( 'foo' , 'bar' ) x( 'yada' , 'yada' ) x( 'bar.foo' , 'bla' ) x_display() x_unset( 'bar' ) x_display() Grab your updated code above. Edited April 21, 2010 by OHB
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now