Dictionaries




Example File


Contents

- What is a Dictionary ?

- What is the purpose of a Dictionary ?

- Where does the Dictionary derive from ?

- How to create a Dictionary ?

- How to populate a Dictionary ?

- Add or replace ?

- Keys

- When is a key unique ?

- Create unique keys automatically

- A list of unique elements

- Items

- The number of elements in a Dictionary

- Check the existence of a key / item in the Dictionary

- How to retrieve 1 element from the Dictionary ?

- How to use the array .Keys ?

- How to use the array .Items ?

- Change the key of an item

- Copy an item inside the Dictionary

- Remove an item from the Dictionary

- Adapt the contents of an item in the Dictionary

- Remove all items from the Dictionary

- Early binding and late binding

- Examples

I. What is a Dictionary ?

A Dictionary in VBA is a collectionobject: you can store all kinds of things in it: numbers, texts, dates, arrays, ranges, variables and objects.
Every item in a Dictionary gets its own unique key.
With that key you can get direct access to the item (reading/writing/adapting).

VBA has several methods to store data:
- a Dictionary
- a Collection
- an array (matrix) variable
- an ActiveX ComboBox
- an ActiveX ListBox
- a Userform control ComboBox
- a Userform control ListBox
- a sortedlist
- an arraylist

Which one to use is dependent of your ultimate goal.
This tutorial doesn't offer an exhaustive comparison of all these methods.
What a Dictionary has to offer will be discussed in detail.
With that knowledge it's easier to compare different methods and to make a choice between them.

An important chracteristic of a Dictionary is the use of unique keys.
It's not necessary to look for an item using a loop or by applying the Excelfunction application.match.
That's an advantage of a Dictionary compared to an array/matrix-variable.

The way in which the Dictionary stores items is comparable to the Collection object.
The Dictionary has some properties/functions/methods like .keys, .items and .removeall, that can be advantageous compared to the use of a collection.
So the preference for a Dictionary over a matrix variable or Collection is dependent of what you want to use it for.

II What is the purpose of a Dictionary ?

You can group all kinds of data in a Dictionary to get easy and quick access to them since they are temporarily stored in memory.
Instead of manipulating data in an Excel-worksheet, a Word Document, or a Powerpoint presentation on the hard disk, you can do that in memory.
No screenrefreshing, no autocalculation, no background backup is necessary, so it will speed up your code considerably.

Generally speaking you can use a Dictionary to store/group data that have a common property (key).
Scattered data can be integrated easily.

A Dictionary can only contain unique keys. That's why you can use the property .keys to create a list of unique strings, numbers or dates.
Although the Dictionary has not been designed for that purpose it's a nice side effect.
The unique keys will not be sorted; their order matches the order in which they have been added to the Dictionary.
If you need a sorted list of unique keys you will have to sort the data before adding them to a Dictionary, or you need to use methods from another VBA library (e.g. system.arraylist or system.orderedlist)
You might need unique lists e.g. as validation list in Excel, the contents of an ActiveX-control (combobox or listbox) or a Userform control (combobox or listbox).

III Where does the Dictionary derive from ?

The Dictionary isn't part of the standard VBA library.
It's an element in the Microsoft Scripting Runtime library.
That library is in the file ..\Windows\system32\scrrun.DLL or in a similar directory
In the VBEditor you can make a reference to this library by enabling 'Microsoft Scripting Runtime' (Menu / Tools / References..)
The reference will be stored in the workbook. If you distirbute the workbook the reference will be distributed simultaneously.

IV How to create a Dictionary ?

There are several methods to do so.
First I'll discuss what you can use a Dictionary for. In the last part (XXII) I will explain the several methods to create a Dictionary into detail.
For simplicity's sake until then I will only use 1 method to illustrate the Dictionary.
It's the simplest and least accident prone.

To create a Dictionary this code suffices

With CreateObject("scripting.Dictionary")

End With

It reads as: create a new object based on the scripting.Dictionary library.
All instructions / properties starting with a dot . between With ... End With refer to the newly created Dictionary

e.g.:
With CreateObject("scripting.dictionary")
.Add "first name", "contents"
End With

The method .Add adds a new item to the Dictionary wtih the key "first name"; the item contains the string "contents"

or e.g.:
With CreateObject("scripting.dictionary")
MsgBox .Count
End With

The message box shows the property .Count of the Dictionary: i.e. the number of items in the Dictionary.

V How to populate a Dictionary

A Dictionary can only be populated one by one.
To do so you need the contents of an item and a unique key.

The contents can be anything: numbers, strings, dates, arrays, ranges, variables, collections, dictionaries, an empty string, nothing and objects

A key can consist of a number, string, date or object, or a variable containing a number, string, date or object
An array can't be used as a key (nor a 1-dimensional array, nor a multidimensional array).
For simplicity's sake I will only use strings as keys to prevent confusion between keys and index numbers.

There are 4 different methods to add an item to a Dictionary
5.1 method .Add

With CreateObject("scripting.dictionary")
.Add "first", "content"
End With
Add an item to the Dictionary; its content is the string "contents", its key is the string "first"


5.2 method .Item( )=

With CreateObject("scripting.dictionary")
.Item("first") = "content"
End With
Add an item to the Dictionary; its content is the string "contents", its key is the string "first"


5.3 method =.Item()

With CreateObject("scripting.dictionary")
x0 = .Item("first")
End With
Put the contents of Item("first") into variable x0
If the item with this key doesn't exist the item will be added to the Dictionary, using the indicated key.
No contents will be assigned to the item.


5.4 method using an objectvariable

Assign a Dictionary to an objectvariable

Set dict_snb = CreateObject("scripting.dictionary")
dict_snb("first") = Date
Add an item to the Dictionary: its content is a Date and its key is "first"


Written alternatively:

Set dict_snb = CreateObject("scripting.dictionary")
dict_snb.Item("first") = Date

VI Add or replace ?

6.1 The key doesn't exist in the Dictionary

If a key doesn't exist a new one is being created and the item will be added.
All methods (.Add, .Item()=, =Item() and the use of an objectvariable()= ) do this identically.


6.2 The key exists in the Dictionary

6.2.1 method .Add

If the keys exists in the Dictionary and if you use the method .Add to add an item to the same key the code errors out.
This is similar to the use of a Collection

With CreateObject("scripting.dictionary")
.Add "first", Date
.Add "first", "new"
End With

6.2.2 method .Item()=

If the key exists the corresponding item will be overwritten/replaced.
Contrary to the method .Add no error is being reported.

With CreateObject("scripting.dictionary")
.Item("first") = Date
MsgBox .Item("first")
.Item("first") = "new"
MsgBox .Item("first")
End With

6.2.3 method =.Item()

The item's content will be read into a variable. Whether the item is empty or not doesn't make a difference.
If the key exists no change will take place in the Dictionary.

With CreateObject("scripting.dictionary")
x0 = .Item("first")
End With

6.2.4 method using an objectvariable

If a key exists the corresponding item will be replaced.
Contrary to the method .Add no error is being reported.

Set dict_snb = CreateObject("scripting.dictionary")
dict_snb("first") = Date
MsgBox dict_snb("first")
dict_snb("first") = "new"
MsgBox dict_snb("first")

Aternative syntax:

Set dict_snb = CreateObject("scripting.dictionary")
dict_snb.Item("first") = Date
MsgBox dict_snb.Item("first")
dict_snb.Item("first") = "new"
MsgBox dict_snb.Item("first")

VII Keys

New keys will be added in the order of the adding process. No other order in keys or items will be made.
A key can consist of a string, a number, a date or an object.
A key can also consist of a variable that is a string, a number, a date, or an object.
A Dictionary can contain several different kinds of keys (string, number, date, object).

Several different keys illustrated

7.1 string key

With CreateObject("scripting.dictionary")
.Items("aa1") = "example 1 "
.Add "aa2", "example 2"
x = .Item("aa3")
dict_snb("aa4") = "example 3"
end with

7.2 number key

With CreateObject("scripting.dictionary")
.Items(2) = "example 4"
.Items(1234589) = "example 5"
.Add 23, "example 6"
x = .Item(45)
dict_snb(56788) = "example 7"
end with

7.3 date key

With CreateObject("scripting.dictionary")
.Items(Date) = "example 8"
.Items(CDate("12-03-2013")) = "example 9"
.Add DateAdd("m", 2, 1), "example 10"
x = .Item(DateSerial(2013, 5, 5))
dict_snb(56788) = "example 11"
end with

7.4 object key

With CreateObject("scripting.dictionary")
.Items(TextBox1) = "example 12"
.Items(TextBox2) = "example 13"
.Add ListBox1, "example 14"
x = .Item(Sheet1)
dict_snb(ComboBox1) = "example 15"
end with

7.5 ActiveX-controls in a worksheet

With CreateObject("scripting.dictionary")
For Each it In Sheets("sheet1").OLEObjects
.Item(it) = it.Name
Next
end with

7.6 Several kinds of keys in 1 Dictionary

With CreateObject("scripting.dictionary")
For Each it In Array("bb1", 12, 45673, Date + 10, sheet4)
.Item(it) = "Item 1" & .Count
Next
end with

VIII When is a key unique ?

There are 2 options:
- a distinction between lowercase and uppercase; ToDay is not identical to the key today
- no distinction between lowercase and uppercase; ToDay is identical to the key today

The property .comparemode indicates the way keys are being compared.
- .comparemode unspecified: lowercase and uppercase are relevant
- .comparemode = BinaryCompare (0) : lowercase and uppercase are relevant
- .comparemode = TextCompare (1) : no relevance to lowercase or uppercase.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "AA1", "Aa1", "aA1", "bb1", "BB1", "Bb1", "bB1")
y = .Item(it)
Next

MsgBox .Count' 8 unique keys
MsgBox Join(.Keys, vbLf)
End With

With CreateObject("scripting.dictionary")
.CompareMode = 0
For Each it In Array("aa1", "AA1", "Aa1", "aA1", "bb1", "BB1", "Bb1", "bB1")
y = .Item(it)
Next

MsgBox .Count' 8 unique keys
MsgBox Join(.Keys, vbLf)
End With

With CreateObject("scripting.dictionary")
.CompareMode = 1
For Each it In Array("aa1", "AA1", "Aa1", "aA1", "bb1", "BB1", "Bb1", "bB1")
y = .Item(it)
Next

MsgBox .Count' 2 unique keys
MsgBox Join(.Keys, vbLf)
End With

IX Create unique keys automatically

Sometimes all items have to be added to a Dictionary.
It doesn't matter in that case which key will be used.
It's necessary to generate a unique key for each item automatically.
You can use the property .count of the Dictionary to create these unique keys.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(.Count) = it & "_contents"
Next
End With
Examples when all items need to be added to the Dictionary:
- to convert a string into a 2-dimensional array: Example 23.9
- to group all ActiveX controls: Example 23.7

X A list of unique elements

By adding items to a Dictionary you simultaneously create a list of unique keys.
So you can use this adding process to create a list of unique values.
It's not even necessary to add any content to items to create such a list.
The method =Item("key") does that.
If the key doesn't exist it wil be added, if it exists nothing will happen.
This method triggers no errors, you should deal with.

With CreateObject("scripting.dictionary")
For Each it In Array(22, 33, 44, 22, 3, 22, 55, 44)
y = .Item(it)
Next
MsgBox .Count ' 5 unique keys because of the replication of 44 and 22
MsgBox Join(.Keys, vbLf)
End With

XI Items

Items will be added sequentially. No ordering of keys or items will take place.
The contents of items can be: numbers, strings, dates, arrays, ranges, variables, collections, dictionaries, an empty string, nothing or objects
As illustration of items with varying contents.
Only a string will be used as key.

11.1 an empty item

With CreateObject("scripting.dictionary")
x0 = .Item("aa1")
End With

11.2 an empty string

With CreateObject("scripting.dictionary")
.Item("aa2") = vbNullString
.Item("aa3") = ""
.Add "aa4", ""
dict_snb("aa5") = vbNullString
End With

11.3 a string

With CreateObject("scripting.dictionary")
.Item("aa6") = "abcde"
.Add "aa7", "abcde"
dict_snb("aa8") = "abcde"
End With

11.4 a non-printable character

With CreateObject("scripting.dictionary")
.Item("aa9") = vbTab
Add "aa10", vbLf
dict_snb("aa11") = vbCrLf
End With

11.5 a number

With CreateObject("scripting.dictionary")
.Item("aa12") = 12345' typename: Integer
.Add "aa13" = 1234589' typename: Long
dict_snb("aa14") = RGB(23, 45, 678) ' typename: Long
End With

11.6 a date (typename: Date)

With CreateObject("scripting.dictionary")
.Item("aa15") = Date
.Add "aa16", CDate("23-04-2012")
dict_snb("aa17") = DateSerial(2013, 10, 12)
End With

11.7 a 1-dimensional array (typename: Variant())

With CreateObject("scripting.dictionary")
.Item("aa18") = Array("aa1", "aa2", "aa3")
Add "aa19", Split("bb1_cc1_dd1", "_")
dict_snb("aa20") = Array("aa1", "aa2", "aa3")
End With

11.8 a multi-dimensional array (typename: Variant())

With CreateObject("scripting.dictionary")
ReDim sn(6, 10)
.Item("aa21") = sn
.Add "aa22", Range("A1:K10")
dict_snb("aa23") = Range("A1:K10").Formula
End With

11.9 an object

The method .Add puts the object into the Dictionary directly.
The methods (.Items = ; objectvariable=) need the instruction 'Set' to assign the object to the Dictionary item.
Otherwise the object's default property would be the content of the Dictionary item.

With CreateObject("scripting.dictionary")
Set .Items("aa24") = Range("A1:K10")' typename: Range
Set dict_snb("aa25") = Range("A1:K10")
.Add "aa26", Range("A1:K10")
End With

11.10 userformcontrols

With CreateObject("scripting.dictionary")
For Each it In Controls
Set .Item(it.Name) = it
.Add it.name& "_", it
Next
End With

11.11 ActiveX-controls

With CreateObject("scripting.dictionary")
For Each it In Sheets("sheet1").OLEObjects
Set .Item(it.Name) = it
.Add it.name & "_", it
Next
End With

11.12 all sheets in a workbook

With CreateObject("scripting.dictionary")
For Each sh In Sheets
Set .Item(sh.Name) = sh
.Add sh.Name & "_", sh
Set dict_snb(sh.Name & "_#") = sh
Next
End With

11.13 A User Defined Type (UDT)k

You can store a User Defined Type (UDT) in an array.
For instance
Public Type translation
dutch As String
german As String
french As String
italian As String
End Type

Sub filling_type_array()
ReDim sn(2) As translation
For j = 0 To 2
sn(j).dutch = Choose(j + 1, "tafel", "wijn", "water")
sn(j).german = Choose(j + 1, "Tisch", "Wein", "Wasser")
sn(j).french = Choose(j + 1, "table", "vin", "eau")
sn(j).italian = Choose(j + 1, "tavola", "vino", "aqua")
Next

MsgBox sn(2).german & " = " & sn(2).italian
end sub

For each array item you can retrieve the data of the type using:
For j=0 to ubound(sn)
MsgBox sn(j).german & " = " & sn(j).italian
next

You can't put a UDT into a Dictionary (nor in a collection).
The code
Dim woord As translation

With CreateObject("scripting.dictionary")
.Add "overzicht", woord
End With
Will return an error code.

With the use of another method we can accomplish an analogous result:
- create a classmodule (e.g named 'trans')
- declare the properties of the class:
Public german As String, french As String, italian As String
- create new instances of the class; assign values to their properties and store these instances in a Dictionary.
For illustration purposes 3 different methods to do so:
NB. the Dutch words serve as keys in the Dictionary.

Sub type_class_in_Dictionary()
With CreateObject("scripting.dictionary")
Set sn = New trans
sn.german = "Tisch"
sn.french = "table"
sn.italian = "tavola"
.Add "tafel", sn

Set .Item("wijn") = New trans
.Item("wijn").german = "Wein"
.Item("wijn").french = "vin"
.Item("wijn").italian = "vino"

.Add "water", New trans
.Item("water").german = "Wasser"
.Item("water").french = "eau"
.Item("water").italian = "aqua"

MsgBox .Item("tafel").french
MsgBox .Item("wijn").italian
End With
End Sub

XII The number of elements in a Dictionary

The Dictionary keeps track of the amount of elements.
The property .Count indicates the number of items in a Dictionary.
Since every item has its own unique key the property .Count also indicates the amount of unique keys.

The function .Items contains all items in the Dictionary, represented by a 1-dimensional array.
The array's property Ubound() indicates the number of elements in an array.
Since the lower limit of a 1-dimensional array is 0, Ubound(.items)+1 also represents the number of elements in a Dictionary.

The function .Keys contains all keys in the Dictionary, represented by a 1-dimensional array.
The array's property Ubound() indicates the number of elements in an array.
Since the lower limit of a 1-dimensional array is 0, Ubound(.Keys)+1 also represents the number of elements in a Dictionary.

Equivalent methods to determine the size of a Dictionary:

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa2")
y = .Item(it)
Next

MsgBox .Count
MsgBox UBound(.Keys) + 1
MsgBox UBound(.Items) + 1
End With

XIII Check the existence of a Key / Item in the Dictionary

If you add an item to a Dictionary using the method .Add and applying an existing key the code will error out.
To prevent this error you can check the key's existence in the Dictionary.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
If Not .Exists(it) Then .Add it, it & "_content"
Next
End With

If you use the method .Item = to add an item to a Dictionary no error message pops up if the keys already exists, the existing item will be replaced.
If that's not what you want you can check the key's existence.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
If Not .Exists(it) Then .Item(it) = it & "_content"
Next
End With

If you want to change the content of an item in the Dictionary it can be very practical to check its existence beforehand.
If the key doesn't exist it will be created automatically when using this code to read the item's content.
y = .Item("first")
If you want to prevent the adding of empty items to the Dictionary checking is required:

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_content"
Next
If .Exists("aa2") Then .Item("aa2") = "new value"
End With

XIV How to retrieve 1 element from the Dictionary ?

14.1 The unique key

A Dictionary has been designed to access (reading /writing) an item directly by key.
Using .item("aa2") you get the contents of the item stored with the key "aa2"

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_content"
Next
MsgBox .Item("aa2")
End With

The property .Item has only the key as argument.


14.2 Use the item's indexnumber

The function .Items is a 1-dimensional array.
The population order of the Dictionary determines the indexnumbers in the array .items.
The first element gets indexnumber 1, the last indexnumber is identical to the property .count.
You'd expect that .items(2) would result in the second item in the Dictionary.
This syntax however doesn't work.
Instead you need a slightly different one: .items()(2).

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_inhoud"
Next

MsgBox .Items()(1) ' the first item
MsgBox .Items()(2) ' the second item
MsgBox .Items()(.count) ' the last item

End With

14.3 The indexnumber of a key

The function .Keys is a 1-dimensional array.
The population order of the Dictionary determines the indexnumbers in the array .items.
The first element gets indexnumber 1, the last indexnumber is identical to the property .count.
You'd expect that .Keys(2) would result in the second key in the Dictionary.
This syntax however doesn't work.
Instead you need a slightly different one: .keys()(2).

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_inhoud"
Next

MsgBox .item(.keys()(1)) ' the first key
MsgBox .item(.keys()(2)) ' the second key
MsgBox .item(.keys()(.count)) ' the last key
End With

14.4 Loop through all keys in the Dictionary and check a condition

Using the function .Keys that contains all keys in a 1-dimensional array

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_content"
Next

For Each it In .Keys
If it = "aa4" Then MsgBox .Item(it)
Next
End With

Using an object variable:

set dict_snb as CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
dict_snb.Item(it) = it & "_content"
Next

For Each it In dict_snb
If it = "aa4" Then MsgBox .Item(it)
Next
End With

14.5 Filter the keys in the Dictionary

We can use the function .Keys that contains all keys in a 1-dimensional array

With CreateObject("scripting.dictionary")
For Each it In Array("aa14", "bb345", "cc392", "rrr987")
.Item(it) = it & "_content"
Next

for each it in Filter(.Keys, "cc")
MsgBox .Item(it)
next

End With

XV How to use the array .Keys ?

15.1 Write all keys to a worksheet

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_content"
Next

Cells(1, 1).Resize(, .Count) = .Keys ' in a row
Cells(1, 1).Resize(.Count) = Application.Transpose(.Keys) ' in a column
End With

15.2 Put all keys into a variable

The variable is an array variable with lower bound (lbound) 0

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_content"
Next

sn = .Keys
End With

15.3 Put all keys into a string

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_contents"
Next

MsgBox Join(.Keys, vbLf)
End With

15.4 Do calculations with the keys

Many Excel worksheetformulae can be applied to the array .keys if the keys consist of numbers.
- the highest keynumber

MsgBox Application.Max(.Keys)

- the lowest keynumber

MsgBox Application.Min(.Keys)

- the third highest key

MsgBox Application.Large(.Keys, 3)

15.5 Filter keys on a certain condition

E.g. filter the year 2012
The method 'filter' converts all keys to strings.
So the filtering condition has to be a string too.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", DateSerial(2012, 12, 3), DateSerial(2012, 12, 4), DateSerial(2012, 12, 8), 22, DateSerial(2013, 12, 3), 44)
.Item(it) = it & "_content"
Next

MsgBox Join(Filter(.Keys, "-2012"), vbLf)
End With

If you want to use the filtered keys to retrieve the corresponding items you will have to reconvert the keys to their original type.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", DateSerial(2012, 12, 3), DateSerial(2012, 12, 4), DateSerial(2012, 12, 8), 22, DateSerial(2013, 12, 3), 44)
.Item(it) = it & "content"
Next

For Each it In Filter(.Keys, "-2012")
MsgBox .Item(CDate(it))
Next
End With

XVI How to use the array .Items ?

16.1 Write the array to a worksheet

NB. This requires the items to be numbers, strings or dates.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_content"
Next

Cells(1, 1).Resize(, .Count) = .Items' in a row
Cells(1, 1).Resize(.Count) = Application.Transpose(.Items) ' in a column
End With

16.2 Put all items into a variable

The variable is an array variable with lower bound(lbound) 0

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = it & "_content"
Next

sn = .Items
End With

16.3 Put all items into a string

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", 22, 3, 22, DateSerial(2013, 12, 3), 44)
.Item(it) = it & "_content"
Next

MsgBox Join(.Items, vbLf)
End With

16.4 Do calculations on the items

Many Excel worksheetformulae are applicable to the array .Items if the items contain numbers.
- the highest item

MsgBox Application.Max(.Items)

- the smallest item

MsgBox Application.Min(.Items)

- the third largest item

MsgBox Application.Large(.Items, 3)

16.5 Filter items on a certain condition

Filter the year 2012
Since the method Filter converts all items to text the filtercondition also has to be a string.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", DateSerial(2012, 12, 3), DateSerial(2012, 12, 4), DateSerial(2012, 12, 8), 22, DateSerial(2013, 12, 3), 44)
.Item(it) = it & "_content"
Next

MsgBox Join(Filter(.Items, "-2012"), vbLf)
End With

XVII Change the key of an item

You can also consider this as a 'replacement' of an item in the Dictionary.

With CreateObject("scripting.dictionary")
.Item("aa") = "this is the first item"
MsgBox .Item("aa")

.Key("aa") = "bb"
MsgBox .Item("bb")
End With

XVIII Copy an item within the Dictionary

You can connect an item to another / new key.

With CreateObject("scripting.dictionary")
.Item("aa") = "The first item"
.Item("bb") = .Item("aa")

MsgBox .Item("bb")
End With

XIX Remove an item from the Dictionary

You need to use the key of an item to remove it with this method.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa2")
.Item(it) = it & "_content"
Next

.Remove "aa3"
End With

XX Change the contents of an item in the Dictionary

20.1 Replace the contents of an item in the Dictionary

The method .Item()= replaces an existing item by a new one with the same key.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa5", "aa6")
.Item(it) = it & "_content"
Next
For Each it In Array("aa1", "aa2", "aa5", "aa6")
.Item(it) = it & "_new content"
Next

MsgBox Join(.Items, "|")
End With
If you sort the items before adding them to a Dictionary you can restrict the items to the most/least recent item, or the item containing the maximum / minimum value.
Since every item is overwritten, only the last item in the sorting order will be kept.
A prerequisite is a sortable property.
See example 23.2 and 23,3.


20.2 Add data to items in the Dictionary

This method applies only to items that contain single numbers, strings or dates.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = "aa"
Next
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = .Item(it) & "_bb"
Next

MsgBox Join(.Items, "|")
End With

20.3 Perform calculations

This method applies only to items that contain single numbers, strings, dates or booleans.


20.3.1 a number

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = 10
Next
For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
.Item(it) = .Item(it) + 40
Next

MsgBox Join(.Items, "|")
End With

20.3.2 a date

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa3", "aa4")
.Item(it) = Date + .Count
Next
For Each it In Array("aa1", "aa2", "aa3", "aa4")
.Item(it) = DateAdd("m", 1, .Item(it))
Next

MsgBox Join(.Items, vbLf)
End With

20.3.3 a boolean

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa2")
.Item(it) = False
Next
For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa2")
.Item(it) = Format(.Item(it), "Yes/No")
Next

MsgBox Join(.Items, "|")
End With

20.4 Change an item that contains an array

20.4.1 a 1-dimensional array

To read an item that contains an array:

With CreateObject("scripting.dictionary")
.Item("aa") = Array("zz1", "zz2", "zz3", "zz4")
MsgBox .Item("aa")(3)
End With

I couldn't find a method to adapt an element in an array of a Dictionary Item directly.
Although the following code doesn't error out, the 4th element in the array isn't been changed by the line .Item("aa")(3) = "the fourth item is " & .Item("aa")(3).

With CreateObject("scripting.dictionary")
.Item("aa") = Array("zz1", "zz2", "zz3", "zz4")
MsgBox .Item("aa")(3)

.Item("aa")(3) = "the fourth item is " & .Item("aa")(3)
MsgBox .Item("aa")(3)
End With

You can use a workaround:
- read the array into a variable
- change one or more elements of this array variable
- replace the Dictionary item's content by the array variable

With CreateObject("scripting.dictionary")
.Item("aa") = Array("zz1", "zz2", "zz3", "zz4")

sn = .Item("aa")
sn(3) = "the fourth element is " & sn(3)
.Item("aa") = sn

MsgBox .Item("aa")(3)
End With

20.4.2 a 2-dimensional array

To read an element from a 2-dimensional array in a Dictionary Item:

With CreateObject("scripting.dictionary")
ReDim sn(3, 4)
For j = 0 To UBound(sn)
For jj = 0 To UBound(sn, 2)
sn(j, jj) = j + 5 * jj
Next
Next

.Item("aa") = sn
MsgBox .Item("aa")(2, 3)
End With

I couldn't find a method to adapt an element in an array of a Dictionary Item directly.

With CreateObject("scripting.dictionary")
ReDim sn(3, 4)
For j = 0 To UBound(sn)
For jj = 0 To UBound(sn, 2)
sn(j, jj) = j + 5 * jj
Next
Next
.Item("aa") = sn

MsgBox .Item("aa")(2, 3)

.Item("aa")(2, 3) = 10 * .Item("aa")(2, 3)
MsgBox .Item("aa")(2, 3)
End With

You can use a workaround
- read the array into a variable
- change one or more elements in this array variable
- replace the Dictionary item's content by this array variable

With CreateObject("scripting.dictionary")
ReDim sn(3, 4)
For j = 0 To UBound(sn)
For jj = 0 To UBound(sn, 2)
sn(j, jj) = j + 5 * jj
Next
Next
.Item("aa") = sn

MsgBox .Item("aa")(2, 3)

sp = .Item("aa")
sp(2, 3) = 10 * sp(2, 3)
.Item("aa") = sp

MsgBox .Item("aa")(2, 3)
End With

XXI Remove all items from the Dictionary

With CreateObject("scripting.dictionary")
For Each it In Array("2", "33", "aa3", "aa4", "aa2")
.Item(it) = it & "_content"
Next
.RemoveAll
End With

XXII Early binding and late binding

Dictionary isn't part of the standard VBA library.
It's in the Microsoft scripting runtime library
It means you have to make a connection to the library in order to be able to use its methods.
The technical term for that connection is 'binding'

You can
- establish the connection to the library before using its code: early binding
- or to connect only if you need the code: late binding

22.1 Late binding

Until now I only used late binding.
That means you create a new object (alternatively called 'instance'), based on a library using the method CreateObject.
The reference to the several VBA libraries isn't very well documented. In the case of a Dictionary it is:

Createobject("scripting.dictionary")
This code performs 2 actions:
- it makes a connection to the library Microsoft Scripting Runtime
- it creates an new object (instance) based on this library.


Both methods (early and late binding) are equivalents. Your preference determines which method to use.
You need to create a new object to be able to work with it.

22.1.1 implicit object

You can make an implicit object using With.... End With

With CreateObject("scripting.dictionary")
.Add "aa1", "today"
End With

22.1.2 object variable

Or you can define an object variable

Set d_snb_ = CreateObject("scripting.dictionary")
d_snb.Add "aa1", "today"

22.2 early binding

The Dictionary is part of the Microsoft Scripting Runtime library
You can make the conection manually in the VBEditor (Alt-F11):
- VBEditor/tools/references: check 'Microsoft Scripting runtime'
You could also use a macro:

Sub M_link_ref_scripting()
ThisWorkbook.VBProject.References.AddFromFile "C:\windows\system32\scrrun.dll"
End Sub

This connection will be stored in the Excel file.
If you distribute the workbook the file will connect automatically to the library if the other system also contains Office.


We need to create a new object (instance), based on the library to be able to work with it.

22.2.1 early binding using an implicit object

Make an implicit object using With ... End With

With New Dictionary
.Add "aa1", "today"
End With

22.2.2. early binding using an objectvariable

The instruction 'Set' is required to assign an object to a variable.

Set d_snb = New Dictionary
d_snb.Add "aa1", "today"

22.2.3.1 early binding using a declared private objectvariable

You can assign a new instance to the objectvariable in the declaration part of a codemodule.
(e.g. the workbook, a worksheet, a userform or a macromodule).

Private d_snb As New Dictionary

Having done that you can use the objectvariable in every procedure in this codemodule.

d_snb.Add "aa1", "PPP"

22.2.3.2 early binding using a declared public objectvariable

If you need the objectvariable to be accessible in the whole project (workbook) you will have to declare it in a macromodule

Public d_snb As New Dictionary
or
In the declaration part:

Public d_snb As Dictionary
In a macro:

Set d_snb = New Dictionary

XXIII Examples

23.1 Delete duplicate rows/arrays

Column A contains the record keys, some of which are duplicates.
Columns B:F etc, contain the data of the records.

Sub M_delete_duplicates()
sn = Sheets("Sheet1").Cells(1).CurrentRegion.resize(,5)

With CreateObject("scripting.dictionary")
For j = 1 To UBound(sn)
.Item(sn(j, 1)) = Application.Index(sn, j, 0)
Next

Sheets("Sheet1").Cells(1, 4).Resize(.Count, UBound(sn, 2)) = Application.Index(.items, 0, 0)
End With
End Sub

23.2 Filter records containing the highest values in column B

Column A contains the record keys.
Column A contains then record keys, some of which are duplicates.
Columns B:F ..... contain the data of the records.
Column B contains the data that can be sorted.

Sub M_filter_records_highest_date_in_columnB()
Sheets("Sheet1").Cells(1).CurrentRegion.Sort Sheets("Sheet1").Cells(1, 2)
sn = Sheets("Sheet1").Cells(1).CurrentRegion

With CreateObject("scripting.dictionary")
For j = 1 To UBound(sn)
.Item(sn(j, 1)) = Application.Index(sn, j, 0)
Next

Sheets("Sheet1").Cells(1, 4).Resize(.Count, UBound(sn, 2)) = Application.Index(.items, 0, 0)
End With
End Sub

23.3 Filter records that contain the smallest value in Column B

Column A contains the record keys, some of which are duplicates.
Columns B: ..... contain the data of the records.
Column B contains the data to be sorted.

Sub M_filter_records_smallestvalue_in_columnB()
Sheets("Sheet1").Cells(1).CurrentRegion.Sort Sheets("Sheet1").Cells(1, 2),2
sn = Sheets("Sheet1").Cells(1).CurrentRegion

With CreateObject("scripting.dictionary")
For j = 1 To UBound(sn)
.Item(sn(j, 1)) = Application.Index(sn, j, 0)
Next

Sheets("Sheet1").Cells(1, 4).Resize(.Count, UBound(sn, 2)) = Application.Index(.items, 0, 0)
End With
End Sub

23.4 records : adding new data

Column A contains the keys of the records.
Column B contains data of the records.
Column E contains (some) keys of the same records as those in column A.
Column F contains data that have to be added to the records in column A.

Sub M_records_aanvullen()
sn = Sheets("Sheet1").Cells(1).CurrentRegion.Resize(, 3)
sp = Sheets("sheet1").Cells(1, 5).CurrentRegion

With CreateObject("scripting.dictionary")
For j = 1 To UBound(sn)
.Item(sn(j, 1)) = Application.Index(sn, j, 0)
Next

For j = 1 To UBound(sp)
st = .Item(sp(j, 1)) st(3) = sp(j, 2) .Item(sn(j, 1)) = Application.Index(st, 0, 0)
Next

Sheets("Sheet1").Cells(1, 10).Resize(.Count, UBound(sn, 2)) = Application.Index(.items, 0, 0)
End With
End Sub

23.5 Integrate several worksheets into one

Sub M_integration()
With createobject("scripting.dictionary")
For each sh in Sheets
.item(sh.name)=sh.usedrange
Next
Sheets.Add.Name="total"

For each it in .items
sheets("total").cells(rows.count,1).end(xlup).offset(1).resize(ubound(it),ubound(it,2))=it
next
end with
End Sub

23.6 Integrate data from several csv files

You will have to adapt the path G:\OF\

Sub M_integratie_csv()
sn = Split(CreateObject("wscript.shell").exec("cmd /c Dir ""G:\OF\*.csv"" /b").stdout.readall, vbCrLf)

With CreateObject("scripting.dictionary")
For j = 0 To UBound(sn)
.Item(sn(j)) = GetObject("G:\OF\" & sn(j)).Sheets(1).UsedRange.Value
GetObject("G:\OF\" & sn(j)).Close False
Next

Sheets.Add.Name = "total"

For Each it In .items
Sheets("total").Cells(Rows.Count, 1).End(xlUp).Offset(1).Resize(UBound(it), UBound(it, 2)) = it
Next
End With
End Sub

23.7 Create a list of unique elements

The adding of items to a Dictionary creates a list of unique keys.
It's not even necessary that the items have any content.
In that case you need the method x0=.Item(key).
If the key doesn't exist, the key will be added; if it exists nothing happens at all.
The method also doesn't produce any error codes.
You can use the unique elements list as validation list (Excel), or to populate a combobox or listbox.

With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa3", "aa2", "aa2", "aa4", "aa5")
y = .Item(it)
Next
Sheets("sheet1").Cells(1, 10).Validation.Add 3, , , Join(.Keys, ",")

Sheets("sheet1").OLEObjects("Combobox1").Object.List = .Keys
Sheets("sheet1").ListBox1.List = .Keys
End With

In a userform:

Private Sub Userform_initialize()
With CreateObject("scripting.dictionary")
For Each it In Array("aa1", "aa2", "aa3", "aa2", "aa2", "aa4", "aa5")
y = .Item(it)
Next

ComboBox1.List = .Keys
ListBox1.List = .Keys
Me("ComboBox2").List = .Keys
Me("ListBox2").List = .Keys
Controls("ComboBox3").List = .Keys
Controls("Listbox3").List = .Keys
End With
End Sub

23.8 check whether all ActiveX controls have some data

Make the ActiveX commandbutton visible if all ActiveX controls are not empty.

Private dict As Dictionary

Sub M_vul()
Set dict = CreateObject("scripting.dictionary")
For Each it In Array("TextBox1", "TextBox2", "textbox3", "combobox4", "combobox5")
Set dict(it) = Sheets("sheet1").OLEObjects(it)
Next
End Sub

Private Sub Textbox1_change()
M_controle
End Sub

Private Sub M_controle()
y = True
For Each it In dict.Items
y = y * (it.Object.Value <> "")
Next
Sheets("sheet1").CommandButton1.Visible = y
End Sub

23.9 check the existence of a worksheet

In a macromodule:
Public dict_sheets As Dictionary

Sub M_sheet_exists()
Set dict_sheets = CreateObject("scripting.dictionary")
For Each it In Sheets
x0 = dict_sheets(it.name)
Next
End Sub

Now you can use everywhere in the VBproject (Workbook):
Sub M_continue()
' - - - If Not dict_sheets.Exists("Sheet10") Then Sheets.Add.Name = "Sheet10"
' - - -
End Sub

23.10 convert a string into a 2-dimensional array

With CreateObject("scripting.dictionary")
For Each it In Split("aa1_aa2_aa3_aa4_aa5|bb1_bb2_bb3_bb4_bb5|cc1_cc2_cc3_cc4_cc5", "|")
.Item(.Count) = Application.Index(Split(it, "_"), 1, 0)
Next

Cells(10, 1).Resize(.Count, UBound(.Item(1))) = Application.Index(.Items, 0, 0)
End With