#include-once ; a hash-table implementation in pure AutoIt for training purposes (by AspirinJunkie) ; builds a new empty hash-table Func _hashtable_Create() ; begin with size 256 like AutoIt Maps Local $aHash[257] = [0] ; return Array Return $aHash EndFunc ;==>_hashtable_Create ; add elements or overwrite if key already exists (@extended = 0: new element; @extended = 1: element overwrite) Func _hashtable_Add(ByRef $aHash, $sKey, $vValue) ; collision handling used: chaining up to 4 subelements, if more then resizing array Local $iElements = $aHash[0], $iSize = UBound($aHash) - 1 ; check if resizing is necessary (parameters need optimization - here only a demonstration) If $iElements > ($iSize / 2) Then __hashtable_resize($aHash, $iElements * 3) $iSize = UBound($aHash) - 1 EndIf ; calculate the hash-value Local $iHash = __hashtable_Hash($sKey) ; calculate the index Local $iIndex = __hashtable_getIndex($iHash, $iSize) ; check if index is already used (collision) Local $vTmp = $aHash[$iIndex] If IsArray($vTmp) Then ; collision ; check if key already exists and overwrite value For $i = 0 To UBound($vTmp) - 1 If $vTmp[$i][0] = $sKey Then $vTmp[$i][1] = $vValue $aHash[$iIndex] = $vTmp Return SetExtended(1, True) EndIf Next ; add new element ReDim $vTmp[UBound($vTmp) + 1][3] ; also save hash-value to boost the resize (to avoid multiple hashing) $vTmp[UBound($vTmp) - 1][0] = $sKey $vTmp[UBound($vTmp) - 1][1] = $vValue $vTmp[UBound($vTmp) - 1][2] = $iHash ; overwrite subarray $aHash[$iIndex] = $vTmp ; too much collisions - so better resize If UBound($vTmp) > 4 Then __hashtable_resize($aHash, $iElements * 3) Else ; no collision Local $aTmp[][3] = [[$sKey, $vValue, $iHash]] $aHash[$iIndex] = $aTmp EndIf ; increment element counter $aHash[0] += 1 Return SetExtended(0, True) EndFunc ;==>_hashtable_Add ; get value by key Func _hashtable_Get(ByRef $aHash, $sKey) Local $iSize = UBound($aHash) - 1 ; calculate the hash-value Local $iHash = __hashtable_Hash($sKey) ; calculate the index Local $iIndex = __hashtable_getIndex($iHash, $iSize) ; linear search for key because of possible element chaining Local $aTmp = $aHash[$iIndex] For $i = 0 To UBound($aTmp) - 1 If $aTmp[$i][0] = $sKey Then Return $aTmp[$i][1] ; element found Next Return SetError(1, $iIndex, Null) ; element not found EndFunc ;==>_hashtable_Get ; remove element by key Func _hashtable_Remove(ByRef $aHash, $sKey) Local $iElements = $aHash[0], $iSize = UBound($aHash) - 1 ; calculate the hash-value Local $iHash = __hashtable_Hash($sKey) ; calculate the index Local $iIndex = __hashtable_getIndex($iHash, $iSize) ; navigate to the element (if exists) Local $aTmp = $aHash[$iIndex] For $i = 0 To UBound($aTmp) - 1 If $aTmp[$i][0] = $sKey Then If UBound($aTmp) > 1 Then ; delete row For $j = $i + 1 To UBound($aTmp) - 1 $aTmp[$j-1][0] = $aTmp[$j][0] $aTmp[$j-1][1] = $aTmp[$j][1] $aTmp[$j-1][2] = $aTmp[$j][2] Next ReDim $aTmp[UBound($aTmp) - 1][UBound($aTmp,2)] $aHash[$iIndex] = $aTmp Else ; only one entry - delete whole subarray $aHash[$iIndex] = "" EndIf ; decrement element counter $aHash[0] -= 1 ; downsize if number of elements is now much smaller than array size ; If $iElements < ($iSize / 5) Then __hashtable_resize($aHash, $iElements / 3) Return SetExtended($iIndex, True) EndIf Next ; element not found Return SetError(1, $iIndex, False) EndFunc ;==>_hashtable_Remove ; get array of all keys Func _hashtable_getKeys(ByRef $aHash) Local $aRet[$aHash[0]] ; create return array Local $iCur = 0, $aTmp ; iterate over all keys For $i = 1 To UBound($aHash) - 1 $aTmp = $aHash[$i] If IsArray($aTmp) And UBound($aTmp, 0) = 2 And UBound($aTmp) > 0 Then For $j = 0 To UBound($aTmp) - 1 $aRet[$iCur] = $aTmp[$j][0] $iCur += 1 Next EndIf Next Return $aRet EndFunc ;==>_hashtable_getKeys ; get array of all values Func _hashtable_getValues(ByRef $aHash) Local $aRet[$aHash[0]] ; create return array Local $iCur = 0, $aTmp ; iterate over all values For $i = 1 To UBound($aHash) - 1 $aTmp = $aHash[$i] If IsArray($aTmp) And UBound($aTmp, 0) = 2 And UBound($aTmp) > 0 Then For $j = 0 To UBound($aTmp) - 1 $aRet[$iCur] = $aTmp[$j][1] $iCur += 1 Next EndIf Next Return $aRet EndFunc ;==>_hashtable_getValues ; get 2D-Array of all key-value pairs Func _hashtable_getKeyValues(ByRef $aHash) Local $aRet[$aHash[0]][2] ; create return array Local $iCur = 0, $aTmp ; iterate over all values For $i = 1 To UBound($aHash) - 1 $aTmp = $aHash[$i] If IsArray($aTmp) And UBound($aTmp, 0) = 2 And UBound($aTmp) > 0 Then For $j = 0 To UBound($aTmp) - 1 $aRet[$iCur][0] = $aTmp[$j][0] $aRet[$iCur][1] = $aTmp[$j][1] $iCur += 1 Next EndIf Next Return $aRet EndFunc ;==>_hashtable_getKeyValues ; count the number of used index (a quality parameter) Func _hashtable_GetUsedIndexCount($aHash) Local $iC = 0 For $i = 1 To UBound($aHash) - 1 If IsArray($aHash[$i]) Then $iC += 1 Next Return $iC EndFunc ; calculate the Index of an hash in a range of size $iSize Func __hashtable_getIndex($iHash, $iSize) Return Abs(Mod($iHash, $iSize)) + 1 ; +1 because Index range = 1..$iSize EndFunc ;==>__hashtable_getIndex ; calculate a Integer-hash value for a key Func __hashtable_Hash($sKey) ; djb Local $hash = 5381 For $c In StringToASCIIArray($sKey) $hash += BitShift($hash, -5) + $c Next Return $hash EndFunc ;==>__hashtable_Hash ; resize a hashtable Func __hashtable_resize(ByRef $aHash, $iNewSize) Local $iElements = $aHash[0], $iSize = UBound($aHash) - 1, $aTmp ; create a empty array with newSize Local $aRet[$iNewSize + 1] = [$iElements] ; iterate over old elements and use _hashtable_Add to new array For $i = 1 To $iSize $aTmp = $aHash[$i] If Not IsArray($aTmp) Then ContinueLoop ; no element at index For $j = 0 To UBound($aTmp) - 1 ; calculate the index Local $iIndex = __hashtable_getIndex($aTmp[$j][2], $iNewSize) ; check if index is already used (collision) Local $vTmp = $aRet[$iIndex] If IsArray($vTmp) Then ; collision ; add new element ReDim $vTmp[UBound($vTmp) + 1][3] $vTmp[UBound($vTmp) - 1][0] = $aTmp[$j][0] $vTmp[UBound($vTmp) - 1][1] = $aTmp[$j][1] $vTmp[UBound($vTmp) - 1][2] = $aTmp[$j][2] ; overwrite subarray $aRet[$iIndex] = $vTmp Else ; no collision Local $aTemp[1][3] = [[$aTmp[$j][0], $aTmp[$j][1], $aTmp[$j][2]]] $aRet[$iIndex] = $aTemp EndIf Next Next ; overwrite old array with new one $aHash = $aRet EndFunc ;==>__hashtable_resize