; #INDEX# =======================================================================================================================
; Title .........: DictionaryHelpers
; Version .......: 0.1
; AutoIt Version : 3.3.14.5
; Language ......: English
; Description ...: Functions to read/write data from/to Excel-xlsx files without the need of having excel installed
; Author(s) .....: AspirinJunkie
; Last changed ..: 2021-05-26
; ===============================================================================================================================

#include-once
#include <Array.au3>



; #FUNCTION# ======================================================================================
; Name ..........: _dic2Array
; Description ...: convert a single dictionary or an 1D-Array of Dictionaries into a 2D-Array
; Syntax ........: _dic2Array(ByRef $oaDicArray, [Const $bSortKeys = False])
; Parameters ....: $oaDicArray     - either a Scripting.Dictionary-object
;                                    or an 1D-Array with dictionaries as elements
;                  $bSortKeys      - true: order of the elements is sorted by their key
;                                    false: no change to the order of the keys
; Return values .: Success - Return 2D-Array[...][2] = [[Key 1, Value 1], [Key 2, Value 2], ...] for single dictionaries
;                            Return 2D-Array with values = rows, keys = columns and key-names in row [0]
;                  Failure - Return Null/False and set @error
; Author ........: AspirinJunkie
; Last changed ..: 2021-07-16
; =================================================================================================
Func _dic2Array(ByRef $oaDicArray, Const $bSortKeys = False)
	If IsArray($oaDicArray) Then ; multiple dictionaries inside an array
		Local $aRet = __dics2Array($oaDicArray, $bSortKeys)
		Return SetError(@error, 2, $aRet)
	ElseIf ObjName($oaDicArray) Then ; single dictionary
		Local $aRet = __dic2Array($oaDicArray, $bSortKeys)
		Return SetError(@error, 1, $aRet)
	Else
		Return SetError(1, 0, Null)
	EndIf
EndFunc   ;==>_dic2Array


; #FUNCTION# ======================================================================================
; Name ..........: _dicDisplay
; Description ...: a _ArrayDisplay-like function for single dictionaries or an array of dictionaries
; Syntax ........: _dicDisplay(ByRef $oaDicArray, [Const $bSortKeys = False, [Const $sTitle = "", [Const $iFlags = 64, [Const $sHeaderElements = "key" & Opt("GUIDataSeparatorChar") & "value"]]]])
; Parameters ....: $oaDicArray     - either a Scripting.Dictionary-object
;                                    or an 1D-Array with dictionaries as elements
;                  $bSortKeys      - true: order of the elements is sorted by their key
;                                    false: no change to the order of the keys
;                  $sTitle         - title of the Window
;                  $iFlags         - flags like $iFlags in _ArrayDisplay()
;                  $sHeaderElements - header items in case of single object
; Return values .: Success - Return True and show a window with the elements of the dictionary
;                  Failure - Return False and set @error
; Author ........: AspirinJunkie
; Last changed ..: 2021-07-16
; =================================================================================================
Func _dicDisplay(ByRef $oaDicArray, Const $bSortKeys = False, Const $sTitle = "", Const $iFlags = 64, Const $sHeaderElements = "key" & Opt("GUIDataSeparatorChar") & "value")
	Local $aArray = _dic2Array($oaDicArray, $bSortKeys)
	If @error Then Return SetError(1, @error, False)

	Switch @extended
		Case 1  ; single dictionary
			_ArrayDisplay($aArray, $sTitle, "", $iFlags, "", $sHeaderElements)
		Case 2  ; multiple dictionaries inside an array
			Local $sHeader = "", $sSep = Opt("GUIDataSeparatorChar")
			For $i = 0 To UBound($aArray, 2) - 1
				$sHeader &= $aArray[0][$i] & $sSep
			Next
			$sHeader = StringTrimRight($sHeader, StringLen($sSep))

			_ArrayDisplay($aArray, $sTitle, "1:", $iFlags, "", $sHeader)
		Case Else
			Return SetError(2, @extended, False)
	EndSwitch

	Return True
EndFunc   ;==>_dicDisplay


; internal helper function only!
; single dictionary to Array[...][2] = [[Key 1, Value 1], [Key 2, Value 2], ...]
Func __dic2Array(ByRef $oDic, Const $bSortKeys = False)
	If ObjName($oDic) <> "Dictionary" Then Return SetError(2, 0, Null)

	Local Const $iN = $oDic.Count
	If $iN <= 1 Then Return SetError(3, $iN, Null)

	Local $aRet[$iN][2], $iC = 0
	Local $aKeys = $oDic.Keys
	If $bSortKeys Then _ArraySort($aKeys)

	For $sKey In $aKeys
		$aRet[$iC][0] = $sKey
		$aRet[$iC][1] = $oDic($sKey)
		$iC += 1
	Next

	Return $aRet
EndFunc   ;==>__dic2Array

; internal helper function only!
; Array of Dictionaries to 2D-Array
Func __dics2Array(ByRef $aArray, Const $bSortKeys = False)
	If UBound($aArray, 0) < 1 Then Return SetError(2, UBound($aArray, 0), Null)

	Local $oKeyIndices = ObjCreate("Scripting.Dictionary"), $iKey = 0

	; iteration 1 for determine number of columns and column indices for Keys
	For $oDic In $aArray
		If ObjName($oDic) <> "Dictionary" Then Return SetError(3, 0, Null)
		For $sKey In $oDic.Keys()
			If Not $oKeyIndices.Exists($sKey) Then
				$oKeyIndices($sKey) = $iKey
				$iKey += 1
			EndIf
		Next
	Next

	Local Const $nRows = UBound($aArray) + 1, $nCols = $oKeyIndices.Count
	Local $aRet[$nRows][$nCols], $iR = 1
	Local $aKeys = $oKeyIndices.Keys

	If $bSortKeys Then
		_ArraySort($aKeys)
		For $i = 0 To UBound($aKeys) - 1
			$oKeyIndices($aKeys[$i]) = $i
		Next
	EndIf

	; fill header row
	For $i = 0 To UBound($aKeys) - 1
		$aRet[0][$i] = $aKeys[$i]
	Next

	; iteration 2 for filling the output array
	For $oDic In $aArray
		For $sKey In $oDic.Keys
			$aRet[$iR][$oKeyIndices($sKey)] = $oDic($sKey)
		Next
		$iR += 1
	Next

	Return $aRet
EndFunc   ;==>__dics2Array


