Reputation: 137
Firstly, I wasn't sure if VBA questions belong here or on SuperUser, apologies if in wrong place.
I am trying to teach myself VBA, and going through various exercises/challenges I've found online. I have made a macro that writes out a multiplication table, its almost certainly very inefficient, but a good starting point on writing loops etc.
My code is as follows:
Sub TimesTable()
Dim TimesTable As Integer
Dim Plot As Integer
Dim Numbers As Long
Dim RowNumbers As Long
Dim StartTime As Double
Dim SecondsElapsed As Double
'Turn off various things to speed up macro
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'Start Timer
StartTime = Timer
ActiveSheet.Cells.ClearContents
'Ask for and read input into variable
TimesTable = InputBox("Enter a Integer to display Times Table")
'Write Axis lines
For Plot = 1 To TimesTable
ActiveSheet.Range("A1").Offset(0, Plot).Value = Plot
ActiveSheet.Range("A1").Offset(Plot, 0).Value = Plot
ActiveSheet.Range("A2").Select
Next
'Start loop 1 to kick off line writing
For RowNumbers = 1 To TimesTable
'Start loop 2 to write actual lines
For Numbers = 1 To TimesTable
ActiveCell.Offset(0, 1).Select
ActiveCell.Value = RowNumbers * Numbers
Next
ActiveCell.Offset(1, -TimesTable).Select
Next
'Turn on screen updating etc again
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
SecondsElapsed = Round(Timer - StartTime, 2)
'Display time taken to run macro
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
End Sub
I've managed to get it all working and learned a few things on the way (mostly how to nest loops and that Integer has a limit), but I was wondering if there is a better way to write each calculation rather than using cell offsets? At the moment it writes the calculation, offsets by one cell to the right, and then when it gets to the end of the line, offsets back to the start of the row, and then goes down one row. I've read that for speed you want to avoid referencing specific cells when possible but struggling with how I would achieve this.
Upvotes: 2
Views: 1426
Reputation: 3139
As already pointed out, .Select
is not necessary and slow. What you want is to refer the Cells directly. Below i have an example without .Offset
and .Select
.
Sub TimesTable()
Dim ws As Worksheet
Dim TimesTable As Long, Plot As Long
Dim Numbers As Long, RowNumbers As Long
Dim StartTime As Double, SecondsElapsed As Double
Dim Cell As Range
Set ws = ActiveSheet
'Turn off various things to speed up macro
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'Start Timer
StartTime = Timer
ws.Cells.ClearContents
'Ask for and read input into variable
TimesTable = 100
'Write Axis lines
With ws
For Plot = 1 To TimesTable
.Cells(Plot + 1, 1).Value = Plot
.Cells(1, Plot + 1).Value = Plot
Next
For Each Cell In .Range(.Cells(2, 2), .Cells(TimesTable + 1, TimesTable + 1))
Cell.Value = .Cells(Cell.Row, 1).Value * .Cells(1, Cell.Column).Value
Next Cell
End With
'Turn on screen updating etc again
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
SecondsElapsed = Round(Timer - StartTime, 2)
'Display time taken to run macro
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
End Sub
I just tried it with TimesTable = 1000
and it saves around 20Secs. The main difference is, that i used a For Each
-Loop instead of 2 For
-Loops. The For Each
goes trough every Cell
in the Range
where the multiplicated values should be. Also to makes things shorter you can use vars for Workbooks, Worksheets, etc.
, also a lot easier to modify afterwards.
Upvotes: 1
Reputation: 37367
Select
is inefficient. Instead of selecting offset cells, try referring to cell like this: Cells(row, column).Value
, with this you can get or set the value of cell on the specified row
and column
(you can use Formula
instead of Value
and many others :) accordingly to your needs ). That should speed up your macro. Generally, in VBA we have Range
type, which can represent a range in a sheet. Thus, you don't need select specific ranges (which slows down execution) in order to refer to them :)
Also, it's better to use Long
instead of Integer
, because all integers are converted to long in VBA anyway, refer to this post.
Upvotes: 1