Does VBA have dictionary structure? Like key<>value array?
11 Answers
Yes.
Set a reference to MS Scripting runtime ('Microsoft Scripting Runtime'). As per @regjo's comment, go to Tools->References and tick the box for 'Microsoft Scripting Runtime'.
Create a dictionary instance using the code below:
Set dict = CreateObject("Scripting.Dictionary")
or
Dim dict As New Scripting.Dictionary
Example of use:
If Not dict.Exists(key) Then
dict.Add key, value
End If
Don't forget to set the dictionary to Nothing
when you have finished using it.
Set dict = Nothing

- 960
- 1
- 13
- 28

- 295,962
- 43
- 465
- 541
-
21This data structure type is provided by the scripting runtime, not by VBA. Basically, VBA can use practically any data structure type that is accessible to it via a COM interface. – David-W-Fenton May 29 '09 at 04:07
-
172Just for the sake of completeness: you need to reference the "Microsoft Scripting Runtime" for this to work (go to Tools->References) and check its box. – regjo Dec 10 '09 at 12:32
-
2Could someone explain to me how the Scripting Runtime's Dictionary object is different from a VBA collection? – David-W-Fenton Apr 29 '11 at 03:38
-
3@ David-W-Fenton: a collection is not keyed – Mitch Wheat Apr 29 '11 at 03:41
-
7Uh, VBA collections ARE keyed. But maybe we have a different definition of `keyed`. – David-W-Fenton Apr 30 '11 at 18:40
-
3In Excel 2010 the reference to "Microsoft Scripting Runtime" is not necessary when using the CreateObject() method. – joweiser Jan 12 '12 at 23:55
-
9I am using Excel 2010... but without the reference to "Microsoft Scripting Runtime" Tools - Ref.. Just doing CreateObject does NOT work. So, @masterjo I think your comment above is wrong. Unless I am missing something.. So, guys Tools -> references is required. – ihightower Jan 21 '12 at 12:26
-
ihightower: would it be possible for you to post a short pastebin of the code that's not working without the reference? I'm really curious! Tested this on a couple different environments and it runs without the reference. – joweiser Jan 22 '12 at 02:44
-
4As an FYI, you can't use the `Dim dict As New Scripting.Dictionary` without the reference. Without the reference, you have to use the late binding `CreateObject` method of instantiating this object. – David Zemens Sep 25 '13 at 13:50
-
1@David-W-Fenton (1) I don't think there is any way to iterate over a Collection's keys. (2) Setting an item (via the default property) with an existing key to a Dictionary will silently overwrite; this is sometimes very useful. (3) It is possible to change the CompareMode of the Dictionary, to be case-sensitive or other CompareMode options. – Zev Spitz Sep 16 '15 at 21:42
-
1@joweiser This is true for previous versions of Office as well. – Zev Spitz Sep 16 '15 at 21:44
-
2@MitchWheat You can also write `dict(key) = value` without explicitly checking if the key exists or not. – Zev Spitz Sep 16 '15 at 21:45
-
3to create the reference to "Microsoft Scripting Runtime" you can also do it by adding this code : `ThisWorkbook.VBProject.References.AddFromFile "C:\windows\system32\scrrun.dll"` – Patrick Lepelletier Jun 12 '16 at 23:30
-
@Andreas Dietrich: the question doesn't mention Excel, so not sure why you are bringing it up. – Mitch Wheat Dec 17 '18 at 10:05
-
1@MitchWheat you are right, but I found it out in the common Excel VBA context and think it may be also interesting to others – Andreas Covidiot Dec 17 '18 at 10:08
-
It's totally irrelevant to the question asked, – Mitch Wheat Dec 17 '18 at 10:15
VBA has the collection object:
Dim c As Collection
Set c = New Collection
c.Add "Data1", "Key1"
c.Add "Data2", "Key2"
c.Add "Data3", "Key3"
'Insert data via key into cell A1
Range("A1").Value = c.Item("Key2")
The Collection
object performs key-based lookups using a hash so it's quick.
You can use a Contains()
function to check whether a particular collection contains a key:
Public Function Contains(col As Collection, key As Variant) As Boolean
On Error Resume Next
col(key) ' Just try it. If it fails, Err.Number will be nonzero.
Contains = (Err.Number = 0)
Err.Clear
End Function
Edit 24 June 2015: Shorter Contains()
thanks to @TWiStErRob.
Edit 25 September 2015: Added Err.Clear()
thanks to @scipilot.

- 4,165
- 3
- 31
- 47

- 9,005
- 2
- 31
- 44
-
5Well done for pointing out the built in Collection object can be used as a dictionary, since the Add method has an optional "key" argument. – Simon Elms May 05 '13 at 12:37
-
12The bad thing about the collection object is, that you cannot check if a key is already in the collection. It'll just throw an error. That's the big thing, i don't like about collections. (i know, that there are workarounds, but most of them are "ugly") – MiVoth Jun 18 '13 at 08:20
-
5Note that the lookup of string keys (eg. c.Item("Key2") ) in the VBA Dictionary IS hashed, but lookup by integer index (eg. c.Item(20) )is not - it's a linear for/next style search and should be avoided. Best to use collections for only string key lookups or for each iteration. – Ben McIntyre Dec 20 '13 at 01:32
-
1
-
4I found a shorter [`Contains`](http://codevba.com/help/collection.htm#.VYVZAjCqopQ): `On Error Resume Next` _ `col(key)` _ `Contains = (Err.Number = 0)` – TWiStErRob Jun 20 '15 at 12:14
-
1Shouldn't you clear `Err` at the end of `Contains()`? Else it would get caught by calling functions' error handling. (Note you cannot therefore use it during error handling without caching the current error being handled.) – scipilot Sep 24 '15 at 22:08
-
@scipilot: That's a good idea. Errors are automatically cleared when hitting `On Error` or `Try/Catch/Finally`, but a user might still have code somewhere that does error detection and sees the leftover stuff from here. – Caleb Hattingh Sep 24 '15 at 23:08
-
1I believe in this case yes. On Error Resume will not raise the error up the stack. This will have the error still set at the call site, but will not 'raise' the error up the call stack. If you use On Error Goto in the function and do the test that way, this will trap the error and the error is local to the function and won't need reset. – PaulG Oct 18 '17 at 19:07
-
7Perhaps the function should be named `ContainsKey`; someone reading only the invocation may confuse it for checking that it contains a particular value. – jpmc26 Feb 22 '18 at 11:57
-
-
I was to have a comma separated key to one value. Can that be done with the a Collection? – Si8 Mar 13 '19 at 14:02
-
@Tarik Looks like that's not necessary. According to docs an error handler is active only "between the occurrence of the error and a Resume, Exit Sub, Exit Function, or Exit Property statement". https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/on-error-statement – Joshua Walsh Aug 04 '20 at 06:06
VBA does not have an internal implementation of a dictionary, but from VBA you can still use the dictionary object from MS Scripting Runtime Library.
Dim d
Set d = CreateObject("Scripting.Dictionary")
d.Add "a", "aaa"
d.Add "b", "bbb"
d.Add "c", "ccc"
If d.Exists("c") Then
MsgBox d("c")
End If

- 709
- 5
- 8
An additional dictionary example that is useful for containing frequency of occurence.
Outside of loop:
Dim dict As New Scripting.dictionary
Dim MyVar as String
Within a loop:
'dictionary
If dict.Exists(MyVar) Then
dict.Item(MyVar) = dict.Item(MyVar) + 1 'increment
Else
dict.Item(MyVar) = 1 'set as 1st occurence
End If
To check on frequency:
Dim i As Integer
For i = 0 To dict.Count - 1 ' lower index 0 (instead of 1)
Debug.Print dict.Items(i) & " " & dict.Keys(i)
Next i
-
1An additional tutorial link is: http://www.kamath.com/tutorials/tut009_dictionary.asp – John M Feb 15 '12 at 15:50
-
This was a very good answer and I used it. However, I found that I couldn't reference the dict.Items(i) or dict.Keys(i) in the loop as you do. I had to store those (item list and keys list) in separate vars before entering the loop and then use those vars to get to the values I needed. Like - allItems = companyList.Items allKeys = companyList.Keys allItems(i) If not, I would get the error: "Property let procedure not defined and property get procedure did not return an object" when attempting to access Keys(i) or Items(i) in the loop. – raddevus Oct 30 '17 at 19:55
Building off cjrh's answer, we can build a Contains function requiring no labels (I don't like using labels).
Public Function Contains(Col As Collection, Key As String) As Boolean
Contains = True
On Error Resume Next
err.Clear
Col (Key)
If err.Number <> 0 Then
Contains = False
err.Clear
End If
On Error GoTo 0
End Function
For a project of mine, I wrote a set of helper functions to make a Collection
behave more like a Dictionary
. It still allows recursive collections. You'll notice Key always comes first because it was mandatory and made more sense in my implementation. I also used only String
keys. You can change it back if you like.
Set
I renamed this to set because it will overwrite old values.
Private Sub cSet(ByRef Col As Collection, Key As String, Item As Variant)
If (cHas(Col, Key)) Then Col.Remove Key
Col.Add Array(Key, Item), Key
End Sub
Get
The err
stuff is for objects since you would pass objects using set
and variables without. I think you can just check if it's an object, but I was pressed for time.
Private Function cGet(ByRef Col As Collection, Key As String) As Variant
If Not cHas(Col, Key) Then Exit Function
On Error Resume Next
err.Clear
Set cGet = Col(Key)(1)
If err.Number = 13 Then
err.Clear
cGet = Col(Key)(1)
End If
On Error GoTo 0
If err.Number <> 0 Then Call err.raise(err.Number, err.Source, err.Description, err.HelpFile, err.HelpContext)
End Function
Has
The reason for this post...
Public Function cHas(Col As Collection, Key As String) As Boolean
cHas = True
On Error Resume Next
err.Clear
Col (Key)
If err.Number <> 0 Then
cHas = False
err.Clear
End If
On Error GoTo 0
End Function
Remove
Doesn't throw if it doesn't exist. Just makes sure it's removed.
Private Sub cRemove(ByRef Col As Collection, Key As String)
If cHas(Col, Key) Then Col.Remove Key
End Sub
Keys
Get an array of keys.
Private Function cKeys(ByRef Col As Collection) As String()
Dim Initialized As Boolean
Dim Keys() As String
For Each Item In Col
If Not Initialized Then
ReDim Preserve Keys(0)
Keys(UBound(Keys)) = Item(0)
Initialized = True
Else
ReDim Preserve Keys(UBound(Keys) + 1)
Keys(UBound(Keys)) = Item(0)
End If
Next Item
cKeys = Keys
End Function

- 1
- 1

- 3,975
- 1
- 25
- 31
All the others have already mentioned the use of the scripting.runtime version of the Dictionary class. If you are unable to use this DLL you can also use this version, simply add it to your code.
https://github.com/VBA-tools/VBA-Dictionary/blob/master/Dictionary.cls
It is identical to Microsoft's version.

- 730
- 1
- 7
- 21
The scripting runtime dictionary seems to have a bug that can ruin your design at advanced stages.
If the dictionary value is an array, you cannot update values of elements contained in the array through a reference to the dictionary.

- 89
- 1
- 1

- 278,309
- 50
- 514
- 539
-
3You can read question ones more: I've asked about VBA: Visual Basic for Application, not for VB, not for VB.Net, not for any other language. – May 27 '09 at 12:15
-
1fessGUID: then again, you should read answers more! This answer can also be used for VBA (in particular, the first link). – Konrad Rudolph May 27 '09 at 12:22
-
5I admit. I read the question too fast. But I did tell him what he needed to know. – Matthew Flaschen May 28 '09 at 03:42
-
1The relevance of .Net is that sooner or later VBA (which is currently a subset of VB6) will end up moving to being a subset of .Net. Besides you should never complain if people give you more than you ask for. It's just ungrateful. – Oorang May 29 '09 at 20:30
-
5@Oorang, there's absolutely no evidence of VBA becoming a subset of VB.NET, backcompat rules in Office - imagine trying to convert every Excel macro ever written. – Richard Gadsden Aug 27 '10 at 14:26
-
2VBA is actually a SUPERSET of VB6. It uses the same core DLL as VB6, but then adds on all sorts of functionality for the specific applications in Office. – David-W-Fenton May 06 '11 at 01:47
-
@David-W-Fenton: If I recall correctly, VBA is actually the language engine that VB6 and its predecessors use. There is a core VBA language and then additional libraries for Office applications like Word, Excel, Access. – Simon Elms May 05 '13 at 12:33
-
The "VB6" link above has suffered link rot. The Wayback Machine has a mostly readable version [here](https://web.archive.org/web/20070216062650/http://support.microsoft.com/kb/187234). For now, VBA documentation for `Scripting.Dictionary` is found at: https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/dictionary-object. – jcb May 14 '19 at 17:03
If by any reason, you can't install additional features to your Excel or don't want to, you can use arrays as well, at least for simple problems. As WhatIsCapital you put name of the country and the function returns you its capital.
Sub arrays()
Dim WhatIsCapital As String, Country As Array, Capital As Array, Answer As String
WhatIsCapital = "Sweden"
Country = Array("UK", "Sweden", "Germany", "France")
Capital = Array("London", "Stockholm", "Berlin", "Paris")
For i = 0 To 10
If WhatIsCapital = Country(i) Then Answer = Capital(i)
Next i
Debug.Print Answer
End Sub

- 99
- 1
- 3
-
2The concept of this answer is sound, but the sample code won't run as written. Each variable needs its own `Dim` keyword, `Country` and `Capital` need to be declared as Variants due to the use of `Array()`, `i` ought to be declared (and must be if `Option Explicit` is set), and the loop counter is going to throw an out of bound error -- safer to use `UBound(Country)` for the `To` value. Also maybe worth noting that while the `Array()` function is a useful shortcut, it's not the standard way to declare arrays in VBA. – jcb Feb 27 '18 at 01:11
VBA can use the dictionary structure of Scripting.Runtime
.
And its implementation is actually a fancy one - just by doing myDict(x) = y
, it checks whether there is a key x
in the dictionary and if there is not such, it even creates it. If it is there, it uses it.
And it does not "yell" or "complain" about this extra step, performed "under the hood". Of course, you may check explicitly, whether a key exists with Dictionary.Exists(key)
. Thus, these 5 lines:
If myDict.exists("B") Then
myDict("B") = myDict("B") + i * 3
Else
myDict.Add "B", i * 3
End If
are the same as this 1 liner - myDict("B") = myDict("B") + i * 3
. Check it out:
Sub TestMe()
Dim myDict As Object, i As Long, myKey As Variant
Set myDict = CreateObject("Scripting.Dictionary")
For i = 1 To 3
Debug.Print myDict.Exists("A")
myDict("A") = myDict("A") + i
myDict("B") = myDict("B") + 5
Next i
For Each myKey In myDict.keys
Debug.Print myKey; myDict(myKey)
Next myKey
End Sub

- 42,633
- 8
- 55
- 100
You can access a non-Native HashTable
through System.Collections.HashTable
.
Represents a collection of key/value pairs that are organized based on the hash code of the key.
Not sure you would ever want to use this over Scripting.Dictionary
but adding here for the sake of completeness. You can review the methods in case there are some of interest e.g. Clone, CopyTo
Example:
Option Explicit
Public Sub UsingHashTable()
Dim h As Object
Set h = CreateObject("System.Collections.HashTable")
h.Add "A", 1
' h.Add "A", 1 ''<< Will throw duplicate key error
h.Add "B", 2
h("B") = 2
Dim keys As mscorlib.IEnumerable 'Need to cast in order to enumerate 'https://stackoverflow.com/a/56705428/6241235
Set keys = h.keys
Dim k As Variant
For Each k In keys
Debug.Print k, h(k) 'outputs the key and its associated value
Next
End Sub
This answer by @MathieuGuindon gives plenty of detail about HashTable and also why it is necessary to use mscorlib.IEnumerable
(early bound reference to mscorlib) in order to enumerate the key:value pairs.

- 83,427
- 12
- 54
- 101