Dictionary object

 2020-07-01    VBA programming    0    228

A dictionary is an object where you can store items of any datatype, and unlike arrays you don't have to set the size of how many items you want to store in the dictionary.
Unlike the collection object, each item in the dictionary object must be associated with a unique key identificator.
More information about array variables.
More information about the Collection object.

The dictionary object:

  • Can store items of any datatype (e.g. string, long, worksheet, object, variant, arrays)
  • Each item must be associated with a unique key, usually this is a string or integer, can't be an array
  • The associated key for each item can be retrieved, e.g. when looping through the dictionary items
  • Usually you would only add items of the same datatype to the dictionary
  • It is possible to add items of mixed datatypes to the dictionary
  • Items can not be added before or after existing items in the dictionary
  • Looping through a dictionary is as fast as a collection when using For Each - Next with the key or item
  • Looping through a dictionary is a bit slower than a collection when using For Each - Next with the key and getting the item


Below is some code examples on how to create a dictionary object, populate it with items and then retrieving one or more items:
Sub Example_Dictionary()
' updated 2020-06-26 by OPE
Dim dict As Object, strKey As String, varKey As Variant, varItem As Variant, i As Long, lngValue As Long, objEmployee As clsEmployee
    ' dictionary object:
    ' can store items of any datatype (e.g. string, long, worksheet, object, variant, arrays)
    ' each item must be associated with a unique key, usually this is a string or integer, can't be an array
    ' the associated key for each item can be retrieved, e.g. when looping through the dictionary items
    ' usually you would only add items of the same datatype to the dictionary
    ' it is possible to add items of mixed datatypes to the dictionary
    ' items can not be added before or after existing items in the dictionary
    ' looping through a dictionary is as fast as a collection when using For Each - Next with the key or item
    ' looping through a dictionary is a bit slower than a collection when using For Each - Next with the key and getting the item
    
    'Set dict = New Dictionary ' create a new dictionary object (using early binding, requires a reference to the Microsoft Scripting Runtime library)
    Set dict = CreateObject("Scripting.Dictionary") ' create a new dictionary object (using late binding, no extra references are necessary)
    dict.CompareMode = 1 ' dictionary must be empty, 0=vbBinaryCompare (default), 1=vbTextCompare, 2=vbDatabaseCompare, -1=vbUseCompareOption
    
    ' add items to a dictionary
    For i = 1 To 10
        strKey = CStr(i) ' the key for the item to add/edit
        dict.Add strKey, i * 100 ' add a new item with a key, an error will occur if the item already exists
    Next i
    
    ' add items to a dictionary
    For i = 1 To 10
        strKey = CStr(i) ' the key for the item to add/edit
        dict(strKey) = i * 100 ' add a new item with a key, if the item already exists it will be updated with the new value
    Next i
    
    ' add items to a dictionary
    For i = 1 To 10
        strKey = CStr(i) ' the key for the item to add/edit
        If dict.Exists(strKey) Then ' check if item already exist
            dict(strKey) = dict(strKey) + 1 ' edit the existing item value
        Else
            'dict.Add strKey, 1 ' add the new item and set the initial value, an error will occur if the item already exists
            dict(strKey) = 1 ' add a new item with a key, if the item already exists it will be updated with the new value
        End If
    Next i
    
    ' items in a dictionary must be added in the desired order, they can't be added before or after other existing items
    dict(111) = 111 ' add a new item with a key, if the item already exists it will be updated with the new value
    dict(222) = 222 ' add a new item with a key, if the item already exists it will be updated with the new value
    dict("333") = 333 ' add a new item with a key, if the item already exists it will be updated with the new value
    dict("444") = 444 ' add a new item with a key, if the item already exists it will be updated with the new value
    
    ' remove items from the dictionary
    dict.Remove "9" ' remove item from dictionary using a key identifier (can't be an "index" number)
    dict.Remove 222 ' remove item from dictionary using a key identifier (can't be an "index" number)
    dict.Remove "444" ' remove item from dictionary using a key identifier (can't be an "index" number)
    
    ' use items in the dictionary
    Debug.Print "Count of items in the dictionary: " & dict.Count
    Debug.Print "This is the value of the item for key ""1"": " & dict("1")
    Debug.Print "This is the value of the item for key ""111"": " & dict(111)
    Debug.Print "This is the value of the item for key ""333"": " & dict("333")
    
    ' update items in the dictionary
    dict("333") = dict("333") + 1 ' you can change the dictionary items
    ' if the dictionary item is an object you can change properties of the object (see example below)
    
    ' loop through the dictionary
    Debug.Print "Dictionary: " & dict.Count
    
    ' using a For Each - Next loop is the preferred and significantly fastest method for looping through all items in a dictionary if the dictionary has many items
    ' if you need the item value only:
    i = 0
    For Each varItem In dict.Items ' varItem must be a Variant
        i = i + 1
        Debug.Print i, varItem ' item "index", item value
    Next varItem
    
    ' if you need the key value only:
    i = 0
    For Each varKey In dict.Keys ' varKey must be a Variant
        i = i + 1
        Debug.Print i, varKey ' item "index", key value
    Next varKey
    
    ' if you need both the key and item value:
    i = 0
    For Each varKey In dict.Keys ' varKey must be a Variant
        i = i + 1
        Debug.Print i, varKey, dict(varKey) ' item "index", item key and item value
    Next varKey
    
    ' using a For Next loop can be extremely slow to loop through all items in a dictionary with many items (10000 or more)
    For i = LBound(dict.Keys) To UBound(dict.Keys)
        Debug.Print i, dict.Keys()(i), dict.Items()(i) ' item "index", item key and item value
    Next i
    
    ' write the dictionary content to a worksheet
    ' assumes that the items are normal datatypes, no objects or arrays
    With Sheet1
        On Error Resume Next
        .Range("A3:B" & .Rows.Count).Clear
        .Range("A3").Formula = "Keys"
        .Range("B3").Formula = "Items"
        .Range("A4").Resize(dict.Count, 1).Formula = Application.Transpose(dict.Keys) ' one-dimensional array, needs to be transposed
        .Range("B4").Resize(dict.Count, 1).Formula = Application.Transpose(dict.Items) ' one-dimensional array, needs to be transposed
        With .Range("A3").CurrentRegion
            .Style = "Borders"
            .Rows(1).Style = "Tableheader"
            .EntireColumn.AutoFit
        End With
        On Error GoTo 0
    End With
    
    ' remove all items in a dictionary (usually not necessary)
    dict.RemoveAll
    Set dict = Nothing ' or simply delete the dictionary
    ' or create a new dictionary
    'Set dict = New Dictionary ' create a new dictionary object (using early binding, requires a reference to the Microsoft Scripting Runtime library)
    Set dict = CreateObject("Scripting.Dictionary") ' create a new dictionary object using late binding
    dict.CompareMode = 1 ' dictionary must be empty, 0=vbBinaryCompare (default), 1=vbTextCompare, 2=vbDatabaseCompare, -1=vbUseCompareOption

    Set objEmployee = New clsEmployee ' create a new object and add values to it
    With objEmployee
        .ID = 1
        .Name = "Peder Aas"
        .Position = "Manager"
        .Salary = 100000
        .StartDate = DateSerial(2000, 1, 1)
    End With
    Set dict(objEmployee.ID) = objEmployee ' add a new item or update an existing one, the use of a key is mandatory

    Set objEmployee = New clsEmployee ' create a new object and add values to it
    With objEmployee
        .ID = 2
        .Name = "Lars Holm"
        .Position = "Supervisor"
        .Salary = 125000
        .StartDate = DateSerial(2001, 1, 1)
    End With
    Set dict(objEmployee.ID) = objEmployee ' add a new item or update an existing one, the use of a key is mandatory

    ' loop through the dictionary
    Debug.Print "Dictionary: " & dict.Count
    ' looping using the items is just as fast as looping through a collection
    For Each varItem In dict.Items ' varItem must be a variant
        varItem.DebugPrint ' assumes that the object has a DebugPrint method
        With varItem ' update object in dictionary
            .Salary = .Salary * 1.05
            .EndDate = DateSerial(Year(Date), 12, 31)
        End With
    Next varItem
    
    ' looping using the keys is a little bit slower than looping through a collection if you need to get the corresponding item too
    For Each varKey In dict.Keys ' varKey must be a variant
        dict(varKey).DebugPrint ' assumes that the object has a DebugPrint method
        With dict(varKey) ' update object in dictionary
            .Salary = .Salary * 1.05
            .EndDate = DateSerial(Year(Date), 12, 31)
        End With
    Next varKey
    
    Debug.Print "After update:"
    For Each varItem In dict.Items
        varItem.DebugPrint ' assumes that the object has a DebugPrint method
    Next varItem

    ' add some items of another datatype to the dictionary
    For i = 1 To 3
        dict.Add CStr(i), i * 100
    Next i

    ' loop through a dictionary with mixed data types
    Debug.Print "Dictionary with mixed datatypes: " & dict.Count
    ' looping using the items is just as fast as looping through a collection
    i = 0
    For Each varItem In dict.Items
        i = i + 1
        If TypeName(varItem) = "clsEmployee" Then
            varItem.DebugPrint
        Else
            Debug.Print "Not an employee: " & i, varItem, "TypeName: " & TypeName(varItem)
        End If
    Next varItem
    
    ' looping using the keys is a little bit slower than looping through a collection if you need to get the corresponding item too
    i = 0
    For Each varKey In dict.Keys
        i = i + 1
        If TypeName(dict(varKey)) = "clsEmployee" Then
            dict(varKey).DebugPrint
        Else
            Debug.Print "Not an employee: " & i, dict(varKey), "TypeName: " & TypeName(dict(varKey))
        End If
    Next varKey
End Sub