' #############################################################################
' EMULATE AN ASSOCIATIVE ARRAY
' BASED OFF SOME DISCUSSION AT
' https://www.qb64.org/forum/index.php?topic=1001.15
' Tries to emulate a simple dictionary / associative array in QB64.
' Some limitations
' 1. FIXED THANKS TO BPLUS! Uses a fixed array size
' 2. Only stores integer values. To store other types, just make a copy
' and modify the types and function names. Currently the
' naming convention is pretty clunky, but it is unambiguous!
' 3. Not very efficient (simply appends new keys to the end of the array,
' and searches for them in linear order)
' 4. FIXED THANKS TO BPLUS! Requires declaring and passing around 2 arrays
' (one for keys, one for values), instead of a single variable.
' Now we just pass one array of user-defined type with key/value.
' 5. No sorting.
'
' Questions
' 1. YES THANKS BPLUS! Instead of a fixed size, can we implement this with
' variable arrays and REDIM?
' 2. How can we get it to store mixed data types?
' We could store the values as string, and have a third array of "type",
' but would we have to manually cast each retrieved value with VAL?
' A lazy way of doing it could be add values to the Dictionary UDT
' to hold each type, so we have:
' Type Dictionary
' Key AS STRING
' Type AS STRING ' specifies type ("$", "%", "&", "!", or "#")
' ' (or make this type int and use constants)
' strValue AS STRING
' intValue AS INTEGER
' lngValue AS LONG
' sngValue AS SINGLE
' dblValue AS DOUBLE
' etc.
' End Type
' and have functions like ReadDictionaryString, WriteDictionaryString,
' ReadDictionaryInt, WriteDictionaryInt, etc. Theoretically one key could
' point to multiple values. Just add what you want. Kind of wasteful,
' and a very lazy way to implement it, but simple multipurpose design?
' We could implement an in-memory database, or structures like STxAxTIC
' discusses here: https://www.qb64.org/forum/index.php?topic=2447.0
' The sky is the limit! So many possibilities, but I just needed a simple
' way to do an associative array / dictionary for a relatively small number
' of keys, so for now I think it's easier to just make separate
' dictionary functions/types for each variable type you want to store.
' 3. Would it be more efficient if we inserted elements in sort order,
' so the dictionary is always sorted? That would speed up read (which would
' find the key with a binary search) but slow down write.
' 4. If we added a sort function, to sort by value OR key?
' Which algorithm would be fast (but not too complex to debug &
' understand) for integer values? For string values?
' 5. What are some other ways this could be better implemented?
' #############################################################################
' =============================================================================
' GLOBAL DECLARATIONS a$=string, i%=integer, L&=long, s!=single, d#=double
' boolean constants
CONST cIntDictMax
= 255 ' lets us limit the size of the dictionary
' =============================================================================
' UDTs
' =============================================================================
' GLOBAL VARIABLES
' =============================================================================
' INITIALIZE
' =============================================================================
' RUN TEST
TestIntDictionary
' =============================================================================
' FINISH
SYSTEM ' return control to the operating system PRINT ProgramName$
+ " finished."
' /////////////////////////////////////////////////////////////////////////////
PRINT "-------------------------------------------------------------------------------" PRINT "Initializing integer-value dictionary..." InitIntDictionary arrDict()
'PRINT "arrDict(0).Value = " + cstr$(arrDict(0).Value)
PRINT:
PRINT DumpIntDictionary$
(arrDict
()): WaitForEnter
PRINT "-------------------------------------------------------------------------------" PRINT "Writing some values..."
sKey = "apple": iValue = 3:
bResult = SaveIntDictionary%(arrDict(), sKey, iValue)
PRINT "SaveIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iValue
) + ") returns " + IIFSTR$
(bResult
, "TRUE", "FALSE")
sKey = "africa": iValue = 1:
bResult = SaveIntDictionary%(arrDict(), sKey, iValue)
PRINT "SaveIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iValue
) + ") returns " + IIFSTR$
(bResult
, "TRUE", "FALSE")
sKey = "zebra": iValue = 0:
bResult = SaveIntDictionary%(arrDict(), sKey, iValue)
PRINT "SaveIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iValue
) + ") returns " + IIFSTR$
(bResult
, "TRUE", "FALSE")
sKey = "xylophone": iValue = 2:
bResult = SaveIntDictionary%(arrDict(), sKey, iValue)
PRINT "SaveIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iValue
) + ") returns " + IIFSTR$
(bResult
, "TRUE", "FALSE")
sKey = "zebra": iValue = 4:
bResult = SaveIntDictionary%(arrDict(), sKey, iValue)
PRINT "SaveIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iValue
) + ") returns " + IIFSTR$
(bResult
, "TRUE", "FALSE")
PRINT:
PRINT DumpIntDictionary$
(arrDict
()): WaitForEnter
PRINT "-------------------------------------------------------------------------------" PRINT "Deleting some keys..."
sKey = "zebra": bResult = DeleteIntDictionaryKey%(arrDict(), sKey)
PRINT "DeleteIntDictionaryKey%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ") returns " + IIFSTR$
(bResult
, "TRUE", "FALSE")
sKey = "nonesuch": bResult = DeleteIntDictionaryKey%(arrDict(), sKey)
PRINT "DeleteIntDictionaryKey%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ") returns " + IIFSTR$
(bResult
, "TRUE", "FALSE")
sKey = "xylophone": bResult = DeleteIntDictionaryKey%(arrDict(), sKey)
PRINT "DeleteIntDictionaryKey%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ") returns " + IIFSTR$
(bResult
, "TRUE", "FALSE")
PRINT:
PRINT DumpIntDictionary$
(arrDict
()): WaitForEnter
PRINT "-------------------------------------------------------------------------------"
sKey = "africa": iIndex = FoundIntDictionaryKey%(arrDict(), sKey)
PRINT "FoundIntDictionaryKey%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ") returns " + cstr$
(iIndex
) + " which evaluates to " + IIFSTR$
(iIndex
, "TRUE", "FALSE")
sKey = "xenophobe": iIndex = FoundIntDictionaryKey%(arrDict(), sKey)
PRINT "FoundIntDictionaryKey%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ") returns " + cstr$
(iIndex
) + " which evaluates to " + IIFSTR$
(iIndex
, "TRUE", "FALSE")
sKey = "APPLE": iIndex = FoundIntDictionaryKey%(arrDict(), sKey)
PRINT "FoundIntDictionaryKey%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ") returns " + cstr$
(iIndex
) + " which evaluates to " + IIFSTR$
(iIndex
, "TRUE", "FALSE")
sKey = "zambia": iIndex = FoundIntDictionaryKey%(arrDict(), sKey)
PRINT "FoundIntDictionaryKey%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ") returns " + cstr$
(iIndex
) + " which evaluates to " + IIFSTR$
(iIndex
, "TRUE", "FALSE")
PRINT:
PRINT DumpIntDictionary$
(arrDict
()): WaitForEnter
PRINT "-------------------------------------------------------------------------------" PRINT "Retrieving values..."
sKey = "africa": iDefault = -1
PRINT "ReadIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iDefault
) + ") returns: " + cstr$
(ReadIntDictionary%
(arrDict
(), sKey
, iDefault
))
sKey = "zebra": iDefault = -3
PRINT "ReadIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iDefault
) + ") returns: " + cstr$
(ReadIntDictionary%
(arrDict
(), sKey
, iDefault
))
sKey = "apple": iDefault = -2
PRINT "ReadIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iDefault
) + ") returns: " + cstr$
(ReadIntDictionary%
(arrDict
(), sKey
, iDefault
))
sKey = "nonesuch": iDefault = -4
PRINT "ReadIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iDefault
) + ") returns: " + cstr$
(ReadIntDictionary%
(arrDict
(), sKey
, iDefault
))
sKey = "xylophone": iDefault = -5
PRINT "ReadIntDictionary%(arrDict(), " + CHR$(34) + sKey
+ CHR$(34) + ", " + cstr$
(iDefault
) + ") returns: " + cstr$
(ReadIntDictionary%
(arrDict
(), sKey
, iDefault
))
PRINT:
PRINT DumpIntDictionary$
(arrDict
()): WaitForEnter
PRINT "-------------------------------------------------------------------------------" PRINT "This concludes the test of the pseudo-associative array / integer dictionary."
WaitForEnter
' /////////////////////////////////////////////////////////////////////////////
INPUT "Press <ENTER> to continue", in$
' /////////////////////////////////////////////////////////////////////////////
SUB InitIntDictionary
(arrDict
() AS IntDictionary
) 'arrDict(0).Value = 0
' /////////////////////////////////////////////////////////////////////////////
iIndex = -1
iIndex = iLoop
' KEY EXISTS, UPDATE VALUE
arrDict(iIndex).Value = iValue
SaveIntDictionary% = TRUE
' KEY DOESN'T EXIST, ADD IT
' IS THERE ROOM?
' DICTIONARY FULL!
SaveIntDictionary% = FALSE
' ADD VALUE AND KEY
arrDict(iIndex).Value = iValue
arrDict
(iIndex
).
Key = sKey
SaveIntDictionary% = TRUE
' /////////////////////////////////////////////////////////////////////////////
iIndex = -1
iIndex = iLoop
FoundIntDictionaryKey% = iIndex
' /////////////////////////////////////////////////////////////////////////////
iIndex = -1
iIndex = iLoop
arrDict
(iLoop
).
Key = arrDict
(iLoop
+ 1).
Key arrDict(iLoop).Value = arrDict(iLoop + 1).Value
bResult = TRUE
bResult = FALSE
DeleteIntDictionaryKey% = bResult
' /////////////////////////////////////////////////////////////////////////////
FUNCTION DumpIntDictionary$
(arrDict
() AS IntDictionary
) sResult = ""
sResult
= sResult
+ "IntDictionary size: " + cstr$
(UBOUND(arrDict
) + 1) + CHR$(13) sResult
= sResult
+ "Item(" + CHR$(34) + arrDict
(iLoop
).
Key + CHR$(34) + ") = " + cstr$
(arrDict
(iLoop
).Value
) + CHR$(13) DumpIntDictionary$ = sResult
' /////////////////////////////////////////////////////////////////////////////
iIndex = -1
iIndex = iLoop
ReadIntDictionary% = arrDict(iIndex).Value
ReadIntDictionary% = iDefault
' /////////////////////////////////////////////////////////////////////////////
' /////////////////////////////////////////////////////////////////////////////
FUNCTION IIF
(Condition
, IfTrue
, IfFalse
)
' /////////////////////////////////////////////////////////////////////////////
FUNCTION IIFSTR$
(Condition
, IfTrue$
, IfFalse$
) IF Condition
THEN IIFSTR$
= IfTrue$
ELSE IIFSTR$
= IfFalse$
' /////////////////////////////////////////////////////////////////////////////