robinsiebler Posted June 7, 2012 Share Posted June 7, 2012 (edited) This is definitely a bug (and Jesus Christ I wish AutoIt had real dictionaries and lists like Python. I could have parsed the INI file and had my way with the data in 5 minutes instead of the hours I have wasted trying to do it in AutoIT). Once again, I have this INI section:[Products] ;internal name = Display Name | Folder Name | Installer C3Communicator=Avistar C3 Communicator|Avistar C3 Communicator|C3CommunicatorSetup.msi CitrixRTME=Citrix RealTime Media Engine|Citrix HDX|Citrix HDX RealTime Media Engine.msi CitrixRTC=Citrix RealTime Connector|Citrix HDX|HDX RealTime Connector LC.msi C3Unified=Avistar C3 Unified|C3 Unified - Microsoft Lync Edition|C3UnifiedLyncSetup.msiThen I have this code:$sConf = @ScriptDir & "Build_Auto_Installer" ; Name of the ini file ( '.ini' will be added automatically) Dim $Folder, $Installer, $Name,$Product, $Products _ReadAssocFromIni($sConf) GetProducts() ;code is in previous post x_display("C3Communicator") $Product = "Avistar C3 Communicator" for $item in $Products MsgBox(0, "", $item) ; displays "C3Communicatior" MsgBox(0, "Product", x($item&'.Name')) ; displays "Avistar C3 Communicator" MsgBox(0, "Product", x($item&'.Name')) ; displays "0". Obviously it should display "Avistar C3 Communicator" if x($item&'.Name') == $Product Then MsgBox(0, "Product", x($item&'.Installer')) ; should display "C3CommunicatorSetup.msi", but always displays "0" EndIf Next Edited June 7, 2012 by robinsiebler Link to comment Share on other sites More sharing options...
ibigpapa Posted October 23, 2013 Share Posted October 23, 2013 (edited) I'm getting an error when attempting to call a hash that doesn't exist.  it appears that the code does not return 0 as it states in the function. (edit 10/23/2013) Well it's not that it fails on a call to an non-existent hash.  It fails if you put a value on the parent.    X('parent',value) x('parent.test)  ----- this will throw the error. Here's the error: <filepath>multidemensionalassociativearray.au3 (51) : ==> Variable must be of type "Object".: ElseIf Not $cur.exists( $last_key ) Then ElseIf Not $cur^ ERROR  Here's the Code To reproduce: #include x('test.1.test','test') x('test.2.test','test2') x('test.2.test.name','non-default name') x('default.name','Default Name') For $index in x('test') $name = x('test.' & $index & '.test.name') If $name = 0 Then $name = x('default.name') EndIf ConsoleWrite('Test.' & $index & '.test:' & @TAB & $name & @CRLF) Next   I'm using the code from the original post.  Most of the time edits are performed to that code so if i have to update it from an addtional post please let me know the number and i'll do so. Just in case here's the code from the original post as of 10.23.2013 #include-once ; #INDEX# ======================================================================================================================= ; Title .........: xHashCollection ; AutoIt Version : 3.3.4.0 ; Language ......: English ; Description ...: Create and use Multidimentional Associative Arrays ; Author ........: OHB ; =============================================================================================================================== 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 ; =============================================================================================================================== 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 ; =============================================================================================================================== 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 ; =============================================================================================================================== 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 ; =============================================================================================================================== 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 Edited October 24, 2013 by ibigpapa Link to comment Share on other sites More sharing options...
Zenepay Posted March 19, 2016 Share Posted March 19, 2016 Please help, I try to use this but it won't work. It keeps return the error : --> Press Ctrl+Alt+Break to Restart or Ctrl+Break to Stop "C:\Users\zenzoploen\Desktop\LineAutoPostNetwork_src\myfunctions.au3" (575) : ==> Variable used without being declared.: Local $cur = $_xHashCollection Local $cur = ^ ERROR ->09:24:22 AutoIt3.exe ended.rc:1 I wonder why? since $_xHashCollection is already declared as Global Link to comment Share on other sites More sharing options...
Jeemo Posted July 18, 2016 Share Posted July 18, 2016 OHB, thank you so much for this UDF. It's helped to make something I'm working on much, much easier to manage. Since I'm relying on this so heavily, I found that simply seeing the contents of my 'x' dictionary in plain text (via x_display()) was becoming increasingly difficult for me to navigate. As such, I added a couple functions that provide output in a similar way as x_display, but using a TreeView control. Thought I'd share it here as a way of giving back a little. If you add the following code to your associative-arrays.au3 file, you can call x_display_treeview() in exactly the same way as x_display() - it even takes an argument of a branch from which to start, as with the original x_display function. I didn't bother getting too fancy with being able to resize the resultant popup window, but feel free to tweak it as needed. I've also configured it to auto-expand every node by default. If that doesn't suit you, just change (or comment out) the GuiCtrlSetState line close to the end. Here's the code:  expandcollapse popupGlobal $DictTreeview Func x_display_treeview( $key = '' ) $text = $key If $key <> '' Then $text &= " " $wHnd = GUICreate( "Array " & $key , 700 , 900, -1, -1) GUISetState( @SW_SHOW , $wHnd ) _x_display_treeview( x( $key )) While 1 If GUIGetMsg() == -3 Then ExitLoop WEnd GUISetState( @SW_HIDE , $wHnd ) GUIDelete( $wHnd ) GUICtrlDelete($DictTreeview) $DictTreeview = 0 EndFunc Func _x_display_treeview($item, $parent = 0) Local $TreeViewItem If Not $DictTreeview > 0 Then $DictTreeview = GUICtrlCreateTreeView(0, 0, 700, 900) If $parent = 0 Then $parent = GUICtrlCreateTreeViewItem("Dictionary", $DictTreeview) Endif If IsObj( $item ) Then For $i in $item _x_display_treeview($item.item($i), GUICtrlCreateTreeViewItem($i, $parent)) Next ElseIf IsArray( $item ) Then For $i in $item _x_display_treeview($item.item($i), GUICtrlCreateTreeViewItem($i, $parent)) Next Else $ParentText = GUICtrlRead($parent, 1) GUICtrlSetData($parent, $ParentText & " = " & $item) EndIf GUICtrlSetState($parent, $GUI_EXPAND) Return $TreeViewItem EndFunc  I hope this comes in handy for someone. Cheers, Jeemo An emoticon is worth a dozen words. Link to comment Share on other sites More sharing options...
qsek Posted October 26, 2020 Share Posted October 26, 2020 (edited) Its 2020 and i still use this very useful UDF. I've come to expand the x_display function (originally only for Dictionarys and Arrays) to also handle Maps and MultiDimensional Arrays with > 2 Dimensions (There was a bug with the original code which would not display those correctly) And the best is that you can nest and mix any combination of Dicts, MultiDim Arrays and Maps. Here is an Example: expandcollapse popupDim $t1[2] = ["A","B"] Dim $m[] Dim $subm[] $m[12345678] = "This" $m["12345678"] = "That" $m.maptest1 = $subm $m.maptest1.submaptest1 = 3.141 $m["maptest1"][111] = "string" $m.maptest1.submaptest3 = $t1 Dim $t2[3][2] = [[1,2],[3,$m],[5,6]] $x = ObjCreate("Scripting.Dictionary") $x.add("main", ObjCreate("Scripting.Dictionary")) $x.Item("main").add( "test1", "hello") $x.Item("main").add( "test2", $t2) $x.add("sub", ObjCreate("Scripting.Dictionary")) $x.Item("sub").add( "sub1", "world") $x.Item("sub").add( "sub2", 3.141) Dim $t[2][3][4] = [[[1,2,3,4],[4,5,6,7],[7,$x,9,0]],[[11,22,33,44],[44,55,66,77],[77,88,99,00]]] Dim $p[4][2][3] = _ [ _ [ _ ["272,00,47","01,47,37","325,56,50"], _ ["325,57,11","04,14,11","04,01,10"] _ ], _ [ _ ["272,00,40","01,33,53","01,41,00"], _ ["325,56,50","04,01,10","272,00,40"] _ ], _ [ _ [$t,"01,41,00","01,47,37"], _ ["323,50,01","04,04,15","272,00,47"] _ ], _ [ _ ["272,00,40","01,41,00","01,47,37"], _ ["323,50,01","325,56,50","01,33,53"] _ ] _ ] ;~ ConsoleWrite( _VarDump($p)& @CRLF) x_display($p, 0, "$p") Exit ; #FUNCTION# ==================================================================================================================== ; Name...........: x_display ; Description ...: Writes the contents of an Associative Array to the Console ; Syntax.........: x_display( $key = '' , $MultiArr3DView = False, $sName = '' ) ; Parameters ....: $sKey - the value to display. If empty, outputs the content of $_xHashCollection ; $MultiArr3DView - display multidimensional arrays in a tiled manner ; $sName - prefix output with user string (for example the varibale name) ; Examples: ; x_display() displays everything ; x_display( 'foo' ) displays the contents of foo ; Authors .......: OHB <me at orangehairedboy dot com>, qsek ; =============================================================================================================================== Func x_display( $key = '' , $MultiArr3DView = False, $sName = '') Local $text If Not IsObj($key) And Not isMap($key) And Not isArray($key)Then $text = $key $text &= StringTrimRight( _x_display($key ,$sName , ' ', 1, '', $MultiArr3DView ) , 2 ) ConsoleWrite($text & @LF) EndFunc ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: _x_display ; Description ...: Itterates through a map/Dictionary/array and builds output for x_display ; Authors .......: OHB <me at orangehairedboy dot com>, qsek ; =============================================================================================================================== Func _x_display( $item , $sName = "", $tab = '', $level = 1, $sArrSubscr = "", $MultiArr3DView = False) Static $_MultiArr3DView = ($MultiArr3DView+0 = True) Local $text If $sName <> "" Then $text &= $sName&":" If IsMap( $item ) Then If $level = 1 Then $text &= ' => '& "Map" $text &= ' {' & @CRLF $aMapKeys = MapKeys($item) For $i In $aMapKeys $save = $item[$i] $a = IsString($i) ? "'" : '' $text &= $tab & "["&$a & $i & $a&"]" & _x_display( $item[$i] ,"" , $tab&" ", IsMap($save) ? $level+1 : 1) Next $text &= StringTrimRight($tab, 2) & '}' ElseIf IsObj( $item ) Then If $level = 1 Then $text = ' => '& ObjName($item, 1) $text &= ' (' & @CRLF For $i In $item $value = $item.item($i) $a = IsString($i) ? "'" : '' $text &= $tab &"["&$a & $i &$a&"]" & _x_display( $value , "", $tab&" " , IsObj($value) ? $level+1 : 1 ) Next $text &= StringTrimRight($tab, 2) & ')' ElseIf IsArray( $item ) Then If $level = 1 Then $text &= ' => '& "Array" $dimensions = UBound( $item , 0 ) For $dimension = 1 To $dimensions $size = UBound( $item , $dimension ) $text &= "[" & $size & "]" Next $text &= " (" & @CRLF $sArrSubscr = ""; clear from possible "sub" flag EndIf ;Loop through all elements within the current dimension For $i = 0 to Ubound($item, $level)-1 If $level < Ubound($item, 0) Then ; we need to go deeper, pass on subscription string. Also pass original array so we end up here again $text &= _x_display( $item , "" ,$tab, $level+1, $sArrSubscr&"[" & $i & "]") Else ; we reached the last dimension, Execute the array subscription string If $_MultiArr3DView And $i > 0 Then If UBound( $item , 0 ) > 1 Then $text &= " , " ; Separation between first Dimension items on one line Else $text &= @CRLF&$tab ; If ony 1 dim array -> put every item on new line EndIf Else $text &= $tab EndIf $value = Execute( "$item" & $sArrSubscr&"[" & $i & "]") $text &= $sArrSubscr&"[" & $i & "]" & _x_display( $value , "" , $tab&" ", 1, "sub") If $_MultiArr3DView And StringRight($text, 2) = @CRLF Then $text &= $tab EndIf Next If $level = 1 Then If $_MultiArr3DView and UBound( $item , 0 ) = 1 Then $text &= @CRLF $text &= StringTrimRight($tab, 2) & ')' If Not $_MultiArr3DView Then $text &= @CRLF EndIf If Not $_MultiArr3DView Then Return $text Else $p = IsString($item) ? '"' : '' ; Put Strings in "" $text = ' = '&$p&$item&$p If $_MultiArr3DView And $sArrSubscr = "sub" Then Return $text EndIf $text &= @CRLF Return $text EndFunc  Output: expandcollapse popup$p: => Array[4][2][3] ( [0][0][0] = "272,00,47" [0][0][1] = "01,47,37" [0][0][2] = "325,56,50" [0][1][0] = "325,57,11" [0][1][1] = "04,14,11" [0][1][2] = "04,01,10" [1][0][0] = "272,00,40" [1][0][1] = "01,33,53" [1][0][2] = "01,41,00" [1][1][0] = "325,56,50" [1][1][1] = "04,01,10" [1][1][2] = "272,00,40" [2][0][0] => Array[2][3][4] ( [0][0][0] = 1 [0][0][1] = 2 [0][0][2] = 3 [0][0][3] = 4 [0][1][0] = 4 [0][1][1] = 5 [0][1][2] = 6 [0][1][3] = 7 [0][2][0] = 7 [0][2][1] => Dictionary ( ['main'] ( ['test1'] = "hello" ['test2'] => Array[3][2] ( [0][0] = 1 [0][1] = 2 [1][0] = 3 [1][1] => Map { [12345678] = "This" ['12345678'] = "That" ['maptest1'] { ['submaptest1'] = 3.141 [111] = "string" ['submaptest3'] => Array[2] ( [0] = "A" [1] = "B" ) } } [2][0] = 5 [2][1] = 6 ) ) ['sub'] ( ['sub1'] = "world" ['sub2'] = 3.141 ) ) [0][2][2] = 9 [0][2][3] = 0 [1][0][0] = 11 [1][0][1] = 22 [1][0][2] = 33 [1][0][3] = 44 [1][1][0] = 44 [1][1][1] = 55 [1][1][2] = 66 [1][1][3] = 77 [1][2][0] = 77 [1][2][1] = 88 [1][2][2] = 99 [1][2][3] = 0 ) [2][0][1] = "01,41,00" [2][0][2] = "01,47,37" [2][1][0] = "323,50,01" [2][1][1] = "04,04,15" [2][1][2] = "272,00,47" [3][0][0] = "272,00,40" [3][0][1] = "01,41,00" [3][0][2] = "01,47,37" [3][1][0] = "323,50,01" [3][1][1] = "325,56,50" [3][1][2] = "01,33,53" )  Same Output but with Option $MultiArr3DView = True: expandcollapse popup$p: => Array[4][2][3] ( [0][0][0] = "272,00,47" , [0][0][1] = "01,47,37" , [0][0][2] = "325,56,50" [0][1][0] = "325,57,11" , [0][1][1] = "04,14,11" , [0][1][2] = "04,01,10" [1][0][0] = "272,00,40" , [1][0][1] = "01,33,53" , [1][0][2] = "01,41,00" [1][1][0] = "325,56,50" , [1][1][1] = "04,01,10" , [1][1][2] = "272,00,40" [2][0][0] => Array[2][3][4] ( [0][0][0] = 1 , [0][0][1] = 2 , [0][0][2] = 3 , [0][0][3] = 4 [0][1][0] = 4 , [0][1][1] = 5 , [0][1][2] = 6 , [0][1][3] = 7 [0][2][0] = 7 , [0][2][1] => Dictionary ( ['main'] ( ['test1'] = "hello" ['test2'] => Array[3][2] ( [0][0] = 1 , [0][1] = 2 [1][0] = 3 , [1][1] => Map { [12345678] = "This" ['12345678'] = "That" ['maptest1'] { ['submaptest1'] = 3.141 [111] = "string" ['submaptest3'] => Array[2] ( [0] = "A" [1] = "B" ) } } [2][0] = 5 , [2][1] = 6 ) ) ['sub'] ( ['sub1'] = "world" ['sub2'] = 3.141 ) ) , [0][2][2] = 9 , [0][2][3] = 0 [1][0][0] = 11 , [1][0][1] = 22 , [1][0][2] = 33 , [1][0][3] = 44 [1][1][0] = 44 , [1][1][1] = 55 , [1][1][2] = 66 , [1][1][3] = 77 [1][2][0] = 77 , [1][2][1] = 88 , [1][2][2] = 99 , [1][2][3] = 0 ) , [2][0][1] = "01,41,00" , [2][0][2] = "01,47,37" [2][1][0] = "323,50,01" , [2][1][1] = "04,04,15" , [2][1][2] = "272,00,47" [3][0][0] = "272,00,40" , [3][0][1] = "01,41,00" , [3][0][2] = "01,47,37" [3][1][0] = "323,50,01" , [3][1][1] = "325,56,50" , [3][1][2] = "01,33,53" ) If you need an even more verbose Output with Types and Ubounds there is also this excellent VarDump UDF from @jchd Edited October 27, 2020 by qsek Teamspeak 3 User Viewer - Quick and functional TS3 Query script, which shows online users.Cached Screenshot Deleter - Deletes older Fraps Screenshots if they exceed a specified limit.Unresolved Topics:Intercept and modify dragdrop text behaviour in scite Link to comment Share on other sites More sharing options...
jchd Posted October 26, 2020 Share Posted October 26, 2020 Nice. May I suggest you dump integral and string keys differently? Local $m[] ; $m is a Map $m[12345678] = "This" $m["12345678"] = "That" vd($m) ; my vardump function yields: Map[2] [12345678] => String (4) 'This' ['12345678'] => String (4) 'That' Â This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
qsek Posted October 27, 2020 Share Posted October 27, 2020 @jchd good suggestion, updated in my post. Teamspeak 3 User Viewer - Quick and functional TS3 Query script, which shows online users.Cached Screenshot Deleter - Deletes older Fraps Screenshots if they exceed a specified limit.Unresolved Topics:Intercept and modify dragdrop text behaviour in scite Link to comment Share on other sites More sharing options...
LWC Posted November 4, 2023 Share Posted November 4, 2023 (edited) On 4/7/2012 at 3:49 AM, qsek said: LWC's ini functions were helpful but i wanted more.expandcollapsepopup  I know it's more than a decade later, but thanks! Nevertheless, it's too bad you liked them and didn't end up using them. I refer mostly to _ReadAssocFromIni - by priority of issues: 1. The following crashes the program when a section is empty: If @error Then Return SetError(1, 0, 0) and thus should be replaced with my original code: If @error Then ; If empty section then ignore (treat as void) ContinueLoop EndIf 2. The following hardcodes the program to just INI files and ignores the fact there are also sub-types like INF: $myIni = $myIni&".ini" I suggest just let the user control the extension and fall back on a default extension: Func _ReadAssocFromIni($myIni = 'config.ini', ... 3. Not everyone want to manage several ini files, so I suggest change: Func _ReadAssocFromIni($myIni = 'config', $mySection = '', $sSep = "|") Local $sIni = StringLeft($myIni,StringInStr($myIni,".")-1) ... x($aSection[$i]&"."&$sectionArray[$x][0], $posS) to: Func _ReadAssocFromIni($myIni = 'config', $mySection = '', $sSep = "|", $multi = True) If $multi Then Local $sIni = StringLeft($myIni,StringInStr($myIni,".")-1) EndIf ... x(($multi ? ($aSection[$i] & ".") : "") & $sectionArray[$x][0], $posS)  On 10/27/2020 at 10:16 PM, qsek said: @jchd good suggestion, updated in my post. Would you considering adding my changes?  Other than that, I know Scripting.Dictionary forces all keys to be unique, but i wonder if there's a way to support duplicated keys using some sort of trick, like if it already exists and the user wishes so, add an index (like turn a secondary this into this2). This interests me because one of my programs got a request to support multiple keys, and obviously it's not their problem it's a shortcoming of Scripting.Dictionary. Edited November 4, 2023 by LWC Added quote Link to comment Share on other sites More sharing options...
qsek Posted November 5, 2023 Share Posted November 5, 2023 (edited) 4 hours ago, LWC said: 1. The following crashes the program when a section is empty: If @error Then Return SetError(1, 0, 0) Im not sure what you mean by "crashing the program" with returning an error from the function. I think this should be the intended return. The Loop iterates the $sectionArray that is either: generated from IniReadSectionNames, which shuld not generate an error because the sections were just read and should exist. And even if not (due to external changing) the user should be informed of that through an error. or is filled with only this one section the user specifically requested in the optional $mySection parameter. In which case the user should be informed if that section does not exist by an error, just like IniReadSectionNames does. If, in your case, he gets an empty section, where should he know, that it is really empty or if it doesnt exist? An error return is the apropriate way imho. 4 hours ago, LWC said: 2. The following hardcodes the program to just INI files and ignores the fact there are also sub-types like INF: $myIni = $myIni&".ini" I suggest just let the user control the extension and fall back on a default extension: Func _ReadAssocFromIni($myIni = 'config.ini', ... Â I intentionally wanted to hide the "extension mangement" from the user so it is more clear, that you should use the same string for the root item as well as the name part of the ini file. But as i was cheking your suggestion, i came to ask myself "Why not both?". If you dont care about the extension, you just use Func _ReadAssocFromIni() or _ReadAssocFromIni("myconfig") like before. In this case ".ini" will be added. If you specify an extension, it will use this to load the file but only the name part for the AssoArray key will be used: Func _ReadAssocFromIni($myIni = 'config.ini', $mySection = '', $sSep = "|") If Not StringInStr($myIni,".") Then $myIni &= ".ini" Local $sIni = StringLeft($myIni,StringInStr($myIni,".")-1) Â 4 hours ago, LWC said: 3. Not everyone want to manage several ini files, so I suggest change: Func _ReadAssocFromIni($myIni = 'config', $mySection = '', $sSep = "|") Local $sIni = StringLeft($myIni,StringInStr($myIni,".")-1) ... x($aSection[$i]&"."&$sectionArray[$x][0], $posS) Â Â The ReadAssocFrom and WriteAssocToIni functions are a subset of The AssoArray UDF. I think most users will work with this UDF mainly to be able to use values in a nested, treepath-like fashion within a script. My goal was to provide a way to store a subset of these variables in ini form if you were using the UDF anyway (this is mainly my usecase, too). So in order to not not conflict with my existing keys in the main dictionary, it is sensible to keep the extra "config" key for your ini management and be able to store other data in the main dictionary without it being written to the config unintentionally. Its also more clear if you access a config value with x() to have the config name within the key string so you know it is a config value that will be stored later eventually. If this would be a separate AssocIni UDF where the user was not intended to use the ini dictionary for something else, this would be more logical. But then, some more things would have to change. Anyway thanks for your suggestions. Â 4 hours ago, LWC said: Other than that, I know Scripting.Dictionary forces all keys to be unique, but i wonder if there's a way to support duplicated keys You are right, dictionarys were not designed like that. Either you solve this by using numbered sub keys and write additional x functions for adding, deleting, and iterating already existing subkeys to be able to use it more "array-like". Or you create an array to store subkeys, and store this array as value of your key. This would enable you to use existing autoit functions for array management, but you would have to unpack/pack the array everytime you want to read/write any values. If you are really fancy, you could incorporate arrayidexes into the dot annotation, so you can use it like x("section.key[3].subkey"), x("section.key[-3]"), x("section.key[+]","value") etc. Edited November 5, 2023 by qsek Teamspeak 3 User Viewer - Quick and functional TS3 Query script, which shows online users.Cached Screenshot Deleter - Deletes older Fraps Screenshots if they exceed a specified limit.Unresolved Topics:Intercept and modify dragdrop text behaviour in scite Link to comment Share on other sites More sharing options...
qsek Posted November 5, 2023 Share Posted November 5, 2023 (edited) @LWC I just discovered that the Json UDF from @Ward works with the Scripting Dictionary and added Dot Notation a while ago, including array management. I did some tests and you absolutely can use x and json function interchangeably. Is this a solution for you usecase? expandcollapse popup#include "Json.au3" #include <AssoArrays.au3> $x = $_xHashCollection x("section","key1") Json_Put($x, ".hello", "world") Json_Put($x, ".foo.bar", "foobar") x_del("foo.bar") x_del("foo") x("main","") ; mixing x with json Json_Put($x, ".main.test[0]", "1111") ; manual adding of elements Json_Put($x, ".main.test[1]", "2222") ; manual adding of elements $a = x("main.test") ; adding with autoit functions by unpacking/packing _ArrayAdd($a, "3333") x("main.test",$a) Json_Put($x, ".main.test["&UBound(x("main.test"))&"]", "1234") ; adding elements at end without unpacking ; iteration For $val In x("main.test") ConsoleWrite("$val: " & $val & @CRLF) Next ; iteration with index For $i = 0 To UBound(x("main.test"))-1 ConsoleWrite(x("main.test")[$i] & @CRLF) Next ; writing without unpacking Json_Put($x, ".main.test[1]", "0000") ; reading without unpacking $val = Json_Get($x, ".main.test[1]") ConsoleWrite("Json_Get: " & $val & @CRLF) x_display() Exit  Edited November 5, 2023 by qsek Teamspeak 3 User Viewer - Quick and functional TS3 Query script, which shows online users.Cached Screenshot Deleter - Deletes older Fraps Screenshots if they exceed a specified limit.Unresolved Topics:Intercept and modify dragdrop text behaviour in scite Link to comment Share on other sites More sharing options...
LWC Posted November 5, 2023 Share Posted November 5, 2023 (edited) Thanks for adding the following! Func _ReadAssocFromIni($myIni = 'config.ini', ... But due to still forcing $sIni, overwriting will occur if you have both test.ini and test.inf, etc.. I don't disagree with your logic, just asking why not simply using my small $multi suggestion. If not, then another way is needed to avoid overwriting. The easiest way would be to just include the extension in there. About crashing the program, I'll rephrase. If your INI file looks like: [section1] this=that [section2] ;Blank or containing comments [section3] foo=bar [section4] what=here ... [section 100] ... Then only section1 will make it in. While technically you might say it's not crash, losing 100 sections is a major issue in my view (even losing "just" one non empty section is a no-go). I mean, your error check knows to avoid a technical crash for empty sections, but it just assumes other sections should be avoided as well. In my version if any section out there is not empty, then it deserves to be read. Last but not least, about the JSON UDF, how to I utilize it if my INI file looks like: [section] this=that this=what How will it treat "this" that has 2 different values? Edited November 5, 2023 by LWC Used note Link to comment Share on other sites More sharing options...
LWC Posted November 9, 2023 Share Posted November 9, 2023 (edited) Well, looks like you didn't take anything at all from my original function, including support for removing mid-sentence comments (e.g. value=this; comment about what is this). So I've decided to just add (or re-add) the following to a customized version of your function: Support for duplicated keys - using your own functions (instead of JSON UDF) Support for central settings (i.e. $multi False - I kept your True as default) Support for not ignoring sections that appear after an empty section Re-added my original function's support for IniReadSectionNames_alt and IniReadSection_alt - to support locked Autorun.inf Re-added my original function's support for removing mid-sentence comments, as I feel it makes no sense to store them in an array Unless you decide to include it in your UDF, I recommend others to download your UDF, since it's amazing overall, but just add my _ReadAssocFromIni_alt to their scripts instead of the UDF _ReadAssocFromIni if they desire the aforementioned benefits. If this interests anyone feel free to ask me for a _WriteAssocToIni_alt too. Unfortunately, for duplicated keys I had to use the UDF _ArrayToString meaning it makes it mandatory to include Array.au3... expandcollapse popup#include <Array.au3> ;read AssocArray from IniFile Section ;returns number of items read - sets @error on failure Func _ReadAssocFromIni_alt($myIni = 'config.ini', $multi = True, $mySection = '', $sSep = "|") If Not StringInStr($myIni,".") Then $myIni &= ".ini" if $multi then Local $sIni = StringLeft($myIni,StringInStr($myIni,".")-1) EndIf If $mySection == '' Then $aSection = IniReadSectionNames_alt($myIni); All sections If @error Then Return SetError(@error, 0, 0) Else Dim $aSection[2] = [1,$mySection]; specific Section EndIf For $i = 1 To UBound($aSection)-1 Local $sectionArray = IniReadSection_alt($myIni, $aSection[$i]) If @error Then ContinueLoop For $x = 1 To $sectionArray[0][0] local $valTemp = ($multi ? $sIni&"." : "")&$aSection[$i]&"."&$sectionArray[$x][0] If x($valTemp) or IsArray(x($valTemp)) Then $sectionArray[$x][1] = (x($valTemp) ? x($valTemp) : _ArrayToString(x($valTemp), $sSep)) & $sSep & $sectionArray[$x][1] EndIf $sectionArray[$x][1] = StringStripWS(StringSplit($sectionArray[$x][1], ";")[1], 3) ; Support for mid-sentence comments ;$sectionArray[$x][1] = StringStripWS($value[1], 3) If StringInStr($sectionArray[$x][1], $sSep) then $posS = _MakePosArray($sectionArray[$x][1], $sSep) Else $posS = $sectionArray[$x][1] EndIf x($valTemp, $posS) Next next Return $sectionArray[0][0] EndFunc ;==>_ReadAssocFromIni Func IniReadSectionNames_alt($hIniLocation) local $aSections = IniReadSectionNames($hIniLocation) if not @error then return $aSections EndIf local $filecontent = FileRead($hIniLocation) if @error Then SetError(@error) else local $aSections = StringRegExp($filecontent, '^|(?m)^\s*\[([^\]]+)', 3) $aSections[0] = UBound($aSections) - 1 return $aSections EndIf EndFunc Func IniReadSection_alt($hIniLocation, $aSection) local $aKV = IniReadSection($hIniLocation, $aSection) if not @error then return $aKV else IniReadSectionNames($hIniLocation) ; In case reading a specific section failed, try reading them all if not @error Then SetError(1) ; Fake an error if reading them all worked, proving reading a specific section isn't the problem else local $filecontent = FileRead($hIniLocation) if @error Then SetError(@error) else local $value = StringRegExp($filecontent, "\Q" & $aSection & ']\E\s+([^\[]+)', 1) If Not IsArray($value) Then SetError(1) ; Fake an error if a section couldn't be found else If StringInStr($value[0], @CRLF, 1, 1) Then $value = StringSplit(StringStripCR($value[0]), @LF) ElseIf StringInStr($value[0], @LF, 1, 1) Then $value = StringSplit($value[0], @LF) Else $value = StringSplit($value[0], @CR) EndIf Local $aKV[1][2] For $xCount = 1 To $value[0] If $value[$xCount] = "" Or StringLeft($value[$xCount], 1) = ";" Then ContinueLoop ReDim $aKV[UBound($aKV) + 1][UBound($aKV, 2)] $value_temp = StringSplit($value[$xCount], "=", 2) $aKV[UBound($aKV) - 1][0] = $value_temp[0] $aKV[UBound($aKV) - 1][1] = $value_temp[1] $aKV[0][0] += 1 Next If $aKV[0][0] = "" Then SetError(1) ; Fake an error if a section is empty return $aKV EndIf EndIf EndIf EndIf EndFunc  Edited March 20 by LWC Removed tests Link to comment Share on other sites More sharing options...
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