Professional Documents
Culture Documents
Excel VBA
APRIL 23, 2015 BY PAU L KELLY ·53 COMMENTS
The following code shows some examples of using the VBA Workbooks collection
Debug.Print Workbooks.Count
Debug.Print Workbooks("Example.xlsm").FullName
' Print the full name of the workbook that was opened second
Debug.Print Workbooks(2).FullName
Collections are similar to arrays so it is important to understand what they are and how the differ to arrays.
What is a Collection?
Collections and arrays are both used to group variables. They both store a set of similar items e.g. a list of student marks or
country names. Using a collection or array allows you to quickly and easily manipulate a large number of items.
In my post on arrays, I explained in simple terms what arrays are and why they are so useful. I will briefly recap this
information here.
If you were storing the marks of one student then you can easily do this using a single variable
mark = sheetMarks.Range("A1")
However most of the time you will have more than one student to deal with. Imagine you want to store the marks of 100
students. If you didn’t use collections or arrays you would need to create a hundred variables – one variable to store the
mark for each student.
Another problem is that you have to use these variables individually. If you want to store 100 marks then you need a line
of code each time you want to store a value to a variable.
mark1 = sheetMarks.Range("A1")
mark2 = sheetMarks.Range("A2")
mark100 = sheetMarks.Range("A100")
As you can see in the above example, writing code like this would mean hundreds of lines of repetitive code. When you
use a collection or array you only need to declare one variable. Using a loop with a collection or arrays means you only
need one line for add or reading values.
If we rewrite the above example using a collection then we only need a few lines of code
Dim c As Range
collMarks.Add c.Value
Next
Collections Vs Arrays?
We have looked at what collections and arrays have in common. So what is the difference and why use one over the other?
The main difference is that with an array you normally set the size once. This means that you know the size before you
start adding elements. Let me explain this with an example.
Student Marks
You want to store information about each student. In this example you can easily count the number of rows to get the
number of students. In other words you know the number of items in advance.
In the example code you can see that we get the number of students by counting the rows. We can then use this to create
an array of the correct size.
Let us now look at a second example where we don’t know the number of items in advance
' Declare
coll.Add "Apple"
coll.Add "Pear"
coll.Remove 1
When you add or remove an item to a collection VBA does all the resizing for you. You don’t have to specify the size or
allocate new spaces. VBA does it under the hood. All you have to do is add an item or remove it.
Another Advantage of Collections
Collections are much easier to use than arrays especially if you are new to programming. Most of the time you do three
things with collections:
So if you are not dealing with a larger number of items then using a Collection can be much neater to use.
A Disadvantage of Collections
Collections are read-only.You can add or remove an item but you cannot change the value of the item. If you are going to
be changing the values in a group of items then you will need to use an array.
Now that we know when and why to use a collection let’s look at how to use one.
As you can see you don’t need to specify the size. Once your collection has been created you can easily add items to it.
You can also declare and then create the collection if and when you need it.
' Declare
' Declare
Endif
The advantage to using this method is minimal. Allocating memory was important back in the 1990’s when computer
memory was limited. Unless you are creating a huge number of collections on a slow PC you will never notice any benefit.
Use Set means the collection will behave differently than when you set the collection to nothing. The next section explains
this.
Sub EmptyColl()
coll.Add "Pear"
End Sub
A subtle point to emphasize here is that when you set the collection to Nothing it is not actually set to nothing. Therefore if
you try to compare it to Nothing it will not work.
Sub EmptyCollSet()
' Create collection
coll.Add "Pear"
End Sub
Dim i As Long
coll.Remove i
Next i
End Sub
Adding items to a Collection
It is simple to add items to a collection. You use the add property followed by the value you wish to add
collFruit.Add "Apple"
collFruit.Add "Pear"
collTotals.Add 45.67
collTotals.Add 34.67
When you add items in this manner they are added to the next available index. In the fruit example, Apple is added to
position 1 and Pear to position 2.
collFruit.Add "Apple"
collFruit.Add "Pear"
collFruit.Add "Apple"
collFruit.Add "Pear"
The order can also be set using the Before or After parameter.
Sub access()
coll.Add "Apple"
coll.Add "Pear"
Debug.Print coll(1)
Debug.Print coll(1)
Debug.Print coll(2)
End Sub
You can also use the Item Property to access an item in the collection. It is the default method of the collection so the
followling lines of code are equivalent
Debug.Print coll(1)
Debug.Print coll.Item(1)
Sub WriteValue()
coll.Add "Apple"
coll(1) = "Pear"
End Sub
collFruit.Add "Apple"
collFruit.Add 45
collFruit.Add #12/12/2017#
This is seldom needed. In VBA the Sheets collections contains sheets of type Worksheet and of type Chart. (To create a
Chart sheet simple right click on any Chart, select Move and select the radio button for New sheet).
The following code displays the type and name of all the sheets in the current workbook. Note to access different type you
need the For Each variable to be a variant or you will get an error.
Sub ListSheets()
Dim sh As Variant
For Each sh In ThisWorkbook.Sheets
Next
End Sub
When you access different items the For Each variable must be a variant. If it’s not you will get an error when you access
a different type than you declared. If we declared sh as a worksheet in the above example it would give an error when we
try to access a sheet of type Chart.
It is rare that you would need a collection of different types but as you can see sometimes it can be useful.
I included the parameter names to make the above example clear. However you don’t need to do this. Just remember the
key is the second parameter and must be a unique string.
The following code shows a second example of using keys
Sub UseKey()
Debug.Print collMark("Betty")
' Print Bill's marks
Debug.Print collMark("Bill")
End Sub
1. If the order changes your code will still access the correct item
2. You can directly access the item without reading through the entire collection
3. It can make you code more readable
In the VBA Workbooks collection it is much better to access the workbook by the key(name) than by the index. The order
is dependent on the order they were opened and so is quite random.
Sub UseAWorkbook()
Debug.Print Workbooks("Example.xlsm").Name
Debug.Print Workbooks(1).Name
End Sub
The first issue is easy to get around. The following code checks if a key exists
On Error Goto EH
coll.Item key
Exists = True
EH:
End Function
Sub TestExists()
End Sub
The second issue is not so easy to get around unless you have a good knowledge of programming.
If you wish to use keys there is an alternative to the Collection. You can use the Dictionary. The Dictionary provides more
functionality to work with keys. You can check if keys exist, update the values at keys, get a list of the keys and so on.
Sub AllWorkbook()
Dim i As Long
For i = 1 To Workbooks.Count
Debug.Print Workbooks(i).Name
Next i
End Sub
You can see that we use the range of 1 to Workbooks.Count. The first item is always in postion one and the last item is
always in the position specified by the Count property of the collection.
The next example prints out all the items in a user created collection.
Sub UserCollection()
collFruit.Add "Apple"
collFruit.Add "Pear"
collFruit.Add "Plum"
' Print all items
Dim i As Long
For i = 1 To collFruit.Count
Debug.Print collFruit(i)
Next i
End Sub
Sub AllWorkbookForEach()
Debug.Print book.Name
Next
End Sub
Sub UseBothLoops()
collFruit.Add "Apple"
collFruit.Add "Pear"
collFruit.Add "Plum"
Dim i As Long
For i = 1 To collFruit.Count
Debug.Print collFruit(i)
Next i
Debug.Print fruit
Next fruit
End Sub
is faster
is neater to write
has one order only – low index to high
The For Loop
is slower
is less neater to write
can access in different order
Speed
The For Each is considered faster than the For Loop. Nowadays this is only an issue if you have a large collection and/or a
slow PC/Network.
Neater
The For Each loop is neater to write especially if you are using nested loops. Compare the following loops. Both print the
names of all the worksheets in open workbooks.
Sub PrintNamesFor()
For i = 1 To Workbooks.Count
For j = 1 To Workbooks(i).Worksheets.Count
Next j
Next i
End Sub
Sub PrintNamesForEach()
Next sh
Next bk
End Sub
The For Each loop is much neater to write and less likely to have errors.
Order
The order of the For Each loop is always from the lowest index to the highest. If you want to get a different order then you
need to use the For Loop. The order of the For Loop can be changed. You can read the items in reverse. You can read a
section of the items or you can read every second item.
Sub ReadRightToLeft()
Dim i As Long
Debug.Print ThisWorkbook.Worksheets(i).Name
Next i
For i = 1 To 3
Debug.Print ThisWorkbook.Worksheets(i).Name
Next i
Debug.Print ThisWorkbook.Worksheets(i).Name
Next i
End Sub
The For loop gives more flexibility here but the reality is that most of the time the basic order is all you need.
Sorting a Collection
There is no built-in sort for the VBA collection. However we can use this QuickSort
lTempLow = first
lTempHi = last
lTempLow = lTempLow + 1
Loop
lTempHi = lTempHi - 1
Loop
vTemp = coll(lTempLow)
coll.Remove lTempLow
coll.Add vTemp, Before:=lTempHi
coll.Remove lTempHi + 1
lTempLow = lTempLow + 1
lTempHi = lTempHi - 1
End If
Loop
End Sub
Sub TestSort()
coll.Add "USA"
coll.Add "Spain"
coll.Add "Belguim"
coll.Add "Ireland"
Dim v As Variant
Debug.Print v
Next
End Sub
Using Collections with Functions and Subs
Using a Collection as a parameter or return value is very easy to do. We will look at them in turn.
Sub UseColl()
coll.Add "Apple"
coll.Add "Orange"
PrintColl coll
End Sub
Debug.Print item
Next
End Sub
You can see how useful the sub PrintColl is in the example. It will print all the elements of ANY collection. The size or
type of element does not matter. This shows how flexible collections are to use.
In the following example, we pass total using both ByVal and ByRef. You can see that after we pass using ByRef the
value has changed in the calling procedure.
Sub PassType()
total = 100
PassByValue total
Debug.Print total
PassByReference total
Debug.Print total
End Sub
total = 555
End Sub
total = 555
End Sub
Using ByVal and ByRef with a Collection is a bit different. If you add or remove item then the collection in the original
caller will also be changed. So the Subs in the following example will both remove the first item of the original collection
coll.Remove 1
End Sub
coll.Remove 1
End Sub
The reason for this is that a Collection variable contains a pointer. This means it contains the address of the collection
rather than the actual collection. So when you add or remove an item you are changing what the pointer is pointing at and
not the pointer itself. However if you change the pointer it will be changed outside of the sub.
You don’t need to worry about pointers. All you need to know is how this affects the behaviour of passing a parameter. If
you set a collection parameter to nothing then the behaviour depends on if you used ByRef or ByVal.
End Sub
End Sub
' NOTE: We do not use New keyword here to create the collection.
End Sub
coll.Add "Plum"
coll.Add "Pear"
End Function
Note: that you don’t use the New keyword when declaring the collection in the sub FruitReport(). This is because the
collection is created in CreateCollection(). When you return the collection you are simple assigning the collection
variable to point to this collection.
Conclusion
Collections are a very useful part of VBA. There are much easier to use than Arrays and are very useful when you are
doing a lot of adding and removing items. They have only four properties: Add, Remove, Count and Item. This makes
them very easy to master.