100

I can't seem to find the documentation explaining how to create a hash table or associative array in VBA. Is it even possible?

Can you link to an article or better yet post the code?

ashleedawg
  • 20,365
  • 9
  • 72
  • 105
Tyler
  • 4,679
  • 12
  • 41
  • 60
  • possible duplicate of [Does VBA have Dictionary structure?](http://stackoverflow.com/questions/915317/does-vba-have-dictionary-structure) – mmmmmm Jan 04 '12 at 14:31
  • 1
    Possible duplicate of [Does VBA have Dictionary Structure?](https://stackoverflow.com/questions/915317/does-vba-have-dictionary-structure) – inetphantom Sep 12 '18 at 11:30

4 Answers4

117

I think you are looking for the Dictionary object, found in the Microsoft Scripting Runtime library. (Add a reference to your project from the Tools...References menu in the VBE.)

It pretty much works with any simple value that can fit in a variant (Keys can't be arrays, and trying to make them objects doesn't make much sense. See comment from @Nile below.):

Dim d As dictionary
Set d = New dictionary

d("x") = 42
d(42) = "forty-two"
d(CVErr(xlErrValue)) = "Excel #VALUE!"
Set d(101) = New Collection

You can also use the VBA Collection object if your needs are simpler and you just want string keys.

I don't know if either actually hashes on anything, so you might want to dig further if you need hashtable-like performance. (EDIT: Scripting.Dictionary does use a hash table internally.)

jtolle
  • 7,023
  • 2
  • 28
  • 50
  • yes - dictionary is the answer. I found the answer on this site, too. http://stackoverflow.com/questions/915317/does-vba-have-dictionary-structure – user158017 Aug 21 '09 at 02:04
  • 2
    That's quite a good answer: but the keys are never objects - what's actually happening is that the default property of the object is being cast as a string and used as the key. This doesn't work if the object has no default property (usually 'name') defined. – Nigel Heffernan Oct 08 '14 at 15:08
  • @Nile, Thanks. I see that you are indeed correct. It also looks like if the object has no default property, then the corresponding dictionary key is `Empty`. I edited answer accordingly. – jtolle Feb 16 '15 at 20:04
  • Several data-structures explained here- http://analystcave.com/excel-vba-dictionary-arrays-and-other-data-structures/#Other_useful_data_structures This post shows how to use .NEXT hashtables in Excel VBA- http://stackoverflow.com/questions/8677949/cannot-iterate-hashtable-in-vba-excel/15313488#15313488 – johny why Aug 29 '16 at 19:02
  • above link typo: .NET, not .NEXT. – johny why Aug 29 '16 at 20:47
  • 2
    re: "the keys are never objects" - that is not the case: you can definitely add an object (or at least, a reference to the object) as a key in a Scripting.Dictionary eg `dict.Add Range("a1"), "test" : arr = dict.keys: Debug.Print TypeName(arr(0))` outputs "Range" – Tim Williams Dec 17 '21 at 22:53
  • 2
    ...this can trip you up when loading a dictionary from a range, since not adding an explicit ".Value" when setting the key can cause all sorts of weird outcomes. – Tim Williams Dec 17 '21 at 22:59
  • _"...the keys are never objects - what's actually happening is that the default property of the object is being cast as a string and used as the key."_ That's true for VBA's native Collection object, but *not* the Dictionary object from the Scripting Runtime. As Tim Williams said, this distinction can lead to nasty bugs if you're not careful. I wrote about the topic in depth here: [The Subtle Dictionary Key Bug](https://nolongerset.com/the-subtle-dictionary-key-bug/?utm_campaign=forums&utm_source=stackoverflow&utm_medium=comment) – mwolfe02 Sep 10 '22 at 05:21
7

Try using the Dictionary Object or the Collection Object.

http://visualbasic.ittoolbox.com/documents/dictionary-object-vs-collection-object-12196

Disco
  • 515
  • 1
  • 6
  • 14
  • 3
    The given link does not work anymore. The content as it was at the moment of original posting can be viewed here: https://web.archive.org/web/20090729034340/http://visualbasic.ittoolbox.com/documents/dictionary-object-vs-collection-object-12196 – pjvleeuwen Dec 31 '18 at 09:41
7

I've used Francesco Balena's HashTable class several times in the past when a Collection or Dictionary wasn't a perfect fit and i just needed a HashTable.

Mark Nold
  • 5,638
  • 7
  • 31
  • 33
5

Here we go... just copy the code to a module, it's ready to use

Private Type hashtable
    key As Variant
    value As Variant
End Type

Private GetErrMsg As String

Private Function CreateHashTable(htable() As hashtable) As Boolean
    GetErrMsg = ""
    On Error GoTo CreateErr
        ReDim htable(0)
        CreateHashTable = True
    Exit Function

CreateErr:
    CreateHashTable = False
    GetErrMsg = Err.Description
End Function

Private Function AddValue(htable() As hashtable, key As Variant, value As Variant) As Long
    GetErrMsg = ""
    On Error GoTo AddErr
        Dim idx As Long
        idx = UBound(htable) + 1

        Dim htVal As hashtable
        htVal.key = key
        htVal.value = value

        Dim i As Long
        For i = 1 To UBound(htable)
            If htable(i).key = key Then Err.Raise 9999, , "Key [" & CStr(key) & "] is not unique"
        Next i

        ReDim Preserve htable(idx)

        htable(idx) = htVal
        AddValue = idx
    Exit Function

AddErr:
    AddValue = 0
    GetErrMsg = Err.Description
End Function

Private Function RemoveValue(htable() As hashtable, key As Variant) As Boolean
    GetErrMsg = ""
    On Error GoTo RemoveErr

        Dim i As Long, idx As Long
        Dim htTemp() As hashtable
        idx = 0

        For i = 1 To UBound(htable)
            If htable(i).key <> key And IsEmpty(htable(i).key) = False Then
                ReDim Preserve htTemp(idx)
                AddValue htTemp, htable(i).key, htable(i).value
                idx = idx + 1
            End If
        Next i

        If UBound(htable) = UBound(htTemp) Then Err.Raise 9998, , "Key [" & CStr(key) & "] not found"

        htable = htTemp
        RemoveValue = True
    Exit Function

RemoveErr:
    RemoveValue = False
    GetErrMsg = Err.Description
End Function

Private Function GetValue(htable() As hashtable, key As Variant) As Variant
    GetErrMsg = ""
    On Error GoTo GetValueErr
        Dim found As Boolean
        found = False

        For i = 1 To UBound(htable)
            If htable(i).key = key And IsEmpty(htable(i).key) = False Then
                GetValue = htable(i).value
                Exit Function
            End If
        Next i
        Err.Raise 9997, , "Key [" & CStr(key) & "] not found"

    Exit Function

GetValueErr:
    GetValue = ""
    GetErrMsg = Err.Description
End Function

Private Function GetValueCount(htable() As hashtable) As Long
    GetErrMsg = ""
    On Error GoTo GetValueCountErr
        GetValueCount = UBound(htable)
    Exit Function

GetValueCountErr:
    GetValueCount = 0
    GetErrMsg = Err.Description
End Function

To use in your VB(A) App:

Public Sub Test()
    Dim hashtbl() As hashtable
    Debug.Print "Create Hashtable: " & CreateHashTable(hashtbl)
    Debug.Print ""
    Debug.Print "ID Test   Add V1: " & AddValue(hashtbl, "Hallo_0", "Testwert 0")
    Debug.Print "ID Test   Add V2: " & AddValue(hashtbl, "Hallo_0", "Testwert 0")
    Debug.Print "ID Test 1 Add V1: " & AddValue(hashtbl, "Hallo.1", "Testwert 1")
    Debug.Print "ID Test 2 Add V1: " & AddValue(hashtbl, "Hallo-2", "Testwert 2")
    Debug.Print "ID Test 3 Add V1: " & AddValue(hashtbl, "Hallo 3", "Testwert 3")
    Debug.Print ""
    Debug.Print "Test 1 Removed V1: " & RemoveValue(hashtbl, "Hallo_1")
    Debug.Print "Test 1 Removed V2: " & RemoveValue(hashtbl, "Hallo_1")
    Debug.Print "Test 2 Removed V1: " & RemoveValue(hashtbl, "Hallo-2")
    Debug.Print ""
    Debug.Print "Value Test 3: " & CStr(GetValue(hashtbl, "Hallo 3"))
    Debug.Print "Value Test 1: " & CStr(GetValue(hashtbl, "Hallo_1"))
    Debug.Print ""
    Debug.Print "Hashtable Content:"

    For i = 1 To UBound(hashtbl)
        Debug.Print CStr(i) & ": " & CStr(hashtbl(i).key) & " - " & CStr(hashtbl(i).value)
    Next i

    Debug.Print ""
    Debug.Print "Count: " & CStr(GetValueCount(hashtbl))
End Sub
Stefan0410
  • 121
  • 1
  • 2
  • 19
    I'm not going to downvote a brand new user who posts code, but usually calling something a "hash table" implies that the underlying implementation is actually a hash table! What you have here is an associative array implemented with a regular array plus a linear search. See here for the difference: http://en.wikipedia.org/wiki/Hash_table – jtolle Oct 14 '11 at 03:20
  • 7
    Indeed. The point of a hash table is the 'hashing' of the key leads to its value's location in the underlying storage (or at least near enough, in the case of duplicate keys allowed), therefore eliminating the need for a potentially costly search. – Cor_Blimey Apr 17 '12 at 19:44
  • 4
    Way too slow for larger hashtables. Adding 17,000 entries takes over 15 seconds. I can add 500,000 in under 6 seconds using dictionary. 500,000 in less than 3 seconds using mscorlib hashtable. – Christopher Thomas Nicodemus Dec 27 '15 at 04:08