Read our blogs, tips and tutorials
Try our exercises or test your skills
Watch our tutorial videos or shorts
Take a self-paced course
Read our recent newsletters
License our courseware
Book expert consultancy
Buy our publications
Get help in using our site
461 attributed reviews in the last 3 years
Refreshingly small course sizes
Outstandingly good courseware
Whizzy online classrooms
Wise Owl trainers only (no freelancers)
Almost no cancellations
We have genuine integrity
We invoice after training
Review 30+ years of Wise Owl
View our top 100 clients
Search our website
We also send out useful tips in a monthly email newsletter ...
Using arrays for faster calculations in Excel VBA |
---|
If you're using VBA to perform calculations over many rows in an Excel workbook, you may find yourself waiting a while for your code to finish running. This blog explains how to use arrays in your code to dramatically speed up the process. |
In this blog
This blog explains how to use arrays in Excel VBA to dramatically increase the performance of code which performs calculations over many rows in a worksheet. You can download the sample file used for this blog here.
To demonstrate the power of arrays we'll use a list of films. The sample worksheet contains nearly half a million rows of film data (made by copying a list of 3,700 films 135 times). You can see a sample of the data in the screenshot below:
Our worksheet contains nearly half a million rows of film data.
For our first example, we'd like to convert the Run Time Minutes values in column C into Run Time Hours values and place them in column D.
An example of the results we want to see.
A conventional way to solve this problem is to loop over the range of cells containing input values and, one-by-one, calculate an output value and write it into the appropriate cell. Here's a basic subroutine to accomplish this:
Sub Minutes_to_Hours()
Dim r As Range
'clear old values from column D
Range("D2", Range("D2").End(xlDown)).Clear
'loop over populated cells in column C
For Each r In Range("C2", Range("C2").End(xlDown))
'calculate and write value to cell in column D
r.Offset(0, 1).Value = Int(r.Value / 60) & "h " & (r.Value Mod 60) & "m"
Next r
End Sub
You could run this code to check that it works but you'll have to time it manually.
To compare the performance of our subroutines, it would be helpful to include code which records how long they take to run. We can use VBA's built-in Timer function to do this. Here's the code for a basic timer system:
Sub Minutes_to_Hours()
Dim r As Range
Dim StartTime As Single
Dim EndTime As Single
'clear old values from column D
Range("D2", Range("D2").End(xlDown)).Clear
'store the start time
StartTime = Timer
'loop over populated cells in column C
For Each r In Range("C2", Range("C2").End(xlDown))
'calculate and write value to cell in column D
r.Offset(0, 1).Value = Int(r.Value / 60) & "h " & (r.Value Mod 60) & "m"
Next r
'store the end time
EndTime = Timer
'print the total time in seconds
Debug.Print "Total seconds = " & (EndTime - StartTime)
End Sub
Running the code gives us the result we wanted in the worksheet:
Column D contains the correct answers.
Running the code a few times gives the following timings in the Immediate window in the VB Editor:
It feels a lot longer than that when you're watching it!
Even without using arrays, there are some simple things you can do to improve the performance of this sort of code. Two standard ways to do this are to disable screen updating and automatic calculation of the workbook. Here's the code to do this:
Sub Minutes_to_Hours()
Dim r As Range
Dim StartTime As Single
Dim EndTime As Single
'disable screen updates and automatic calculation
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'clear old values from column D
Range("D2", Range("D2").End(xlDown)).Clear
'store the start time
StartTime = Timer
'loop over populated cells in column C
For Each r In Range("C2", Range("C2").End(xlDown))
'calculate and write value to cell in column D
r.Offset(0, 1).Value = Int(r.Value / 60) & "h " & (r.Value Mod 60) & "m"
Next r
'store the end time
EndTime = Timer
'print the total time in seconds
Debug.Print "Total seconds = " & (EndTime - StartTime)
'enable screen updates and automatic calculation
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.Calculate
End Sub
After remembering to re-enable screen updates and automatic calculation at the end of the procedure, here are the results of running it a few more times:
Shaving five seconds off the time is a decent improvement.
It's an impressive improvement, but we can do better!
What really slows down our code is writing each answer individually to each cell, one-by-one. This is where an array can help by storing all the answers in memory and then writing them into the cells in one step at the end of the procedure. There are three basic steps required to do this:
Load the cell values into an array.
Loop over the array, calculating the new values.
Write the array contents into the output cells.
Here's the basic code required to do this:
Sub Minutes_to_Hours_Array()
Dim StartTime As Single
Dim EndTime As Single
'an empty variable to hold the array
Dim RunTimes
'a variable to count through the array
Dim n As Long
'clear old values from column D
Range("D2", Range("D2").End(xlDown)).Clear
'store the start time
StartTime = Timer
'STEP 1 - store all the input values in the array
RunTimes = Range("C2", Range("C2").End(xlDown))
'STEP 2 - loop over the array and calculate the new values
For n = LBound(RunTimes, 1) To UBound(RunTimes, 1)
'calculate and write value back into the array
RunTimes(n, 1) = Int(RunTimes(n, 1) / 60) & "h " & (RunTimes(n, 1) Mod 60) & "m"
Next n
'STEP 3 - write the array into the output range
Range("D2").Resize(UBound(RunTimes, 1)) = RunTimes
'store the end time
EndTime = Timer
'print the total time in seconds
Debug.Print "Total seconds = " & (EndTime - StartTime)
End Sub
Running the code a few times shows a significant improvement in performance!
Consistently taking just over a second to process half a million rows is impressive!
If you're interested in watching how the array is populated, you can step through your code by pressing the F8 key repeatedly to execute each line, one at a time. Do this until you see the following line highlighted in yellow:
Click in the subroutine and press F8 until you reach this line.
Before executing the highlighted line, open the Locals window by choosing View | Locals Window from the VB Editor menu.
You can see the RunTimes variable is currently empty.
When you press F8 again to execute the highlighted line, you should see the RunTimes array is instantly populated with the values of the cells:
The RunTimes variable holds a 2-dimensional array of values.
VBA defines the dimensions of the array according to the range of cells used to populate it. In our example, the first dimension has 499,500 elements (equal to the number of rows) and the second dimension contains 1 element (equal to the number of columns).
You can click the + symbol next to the array to see the contents of the individual elements.
Expand each element to see its contents.
Continue pressing the F8 key to step through the For loop:
Continue pressing F8 to cycle through this loop.
As you do this, you should see the contents of each array element changing:
One element changes for each iteration of the loop.
Importantly at this point, no values have been written to the cells in the worksheet.
All the values are held in the array.
When you get bored of pressing F8, you can press F5 to run the subroutine to the end. The contents of the array are written into the worksheet when the following line is executed:
This code refers to a range of cells equal in height to the number of elements in the first dimension of the RunTimes array.
The above example is relatively straightforward as the size of the input array matches the size of the output array. Things are a little more complicated when the number of inputs and outputs differ. Let's say that we wanted to calculate the profit for each film by subtracting the budget from the box office takings.
We want the results to appear in column G.
In this example, we can load the contents of columns E and F into a single array with 499,500 elements in the first dimension and 2 elements in the second dimension. The array which holds the answers needs the first dimension to be the same size, but the second dimension should only have 1 element.
The two arrays have a different number of elements in the second dimension.
One way to achieve this is to use the ReDim statement to re-dimension the Outputs array once we know the size of the Inputs array. Here's the basic code you can use to do this:
Sub Calculate_Profit()
'empty variables to hold the inputs and outputs
Dim Inputs, Outputs
'STEP 1a - store all the input values in the array
Inputs = Range("E2", Range("F2").End(xlDown))
'STEP 1b - set the size of the Outputs array
ReDim Outputs(1 To UBound(Inputs, 1), 1 To 1)
End Sub
The rest of the process is similar to the previous example. The main difference is that we write the answers to the Outputs array, rather than changing the contents of the Inputs array. Here's the full set of code to achieve this:
Sub Calculate_Profit()
Dim StartTime As Single
Dim EndTime As Single
'empty variables to hold the inputs and outputs
Dim Inputs, Outputs
'a variable to count through the array
Dim n As Long
'clear old values from column G
Range("G2", Range("G2").End(xlDown)).Clear
'store the start time
StartTime = Timer
'STEP 1a - store all the input values in the array
Inputs = Range("E2", Range("F2").End(xlDown))
'STEP 1b - set the size of the Outputs array
ReDim Outputs(1 To UBound(Inputs, 1), 1 To 1)
'STEP 2 - loop over the array and calculate the new values
For n = LBound(Inputs, 1) To UBound(Inputs, 1)
'calculate profit and write to Outputs array
Outputs(n, 1) = Inputs(n, 2) - Inputs(n, 1)
Next n
'STEP 3 - write the array into the output range
Range("G2").Resize(UBound(Outputs, 1)) = Outputs
'store the end time
EndTime = Timer
'print the total time in seconds
Debug.Print "Total seconds = " & (EndTime - StartTime)
End Sub
If you step through the code you can watch the Outputs array being populated, one element at a time.
Image caption
Running the code a few times gives the following results in the Immediate window:
Under half a second to process half a million rows.
Finally, the most important thing is that all the answers appear in the worksheet:
The results after running the code.
So there you have it: using arrays to process calculations in VBA can save you an enormous amount of time!
Some other pages relevant to the above blog include:
Kingsmoor House
Railway Street
GLOSSOP
SK13 2AA
Landmark Offices
99 Bishopsgate
LONDON
EC2M 3XD
Holiday Inn
25 Aytoun Street
MANCHESTER
M1 3AE
© Wise Owl Business Solutions Ltd 2025. All Rights Reserved.