Useful VBA Utilities for Microsoft Excel & Data Handling
Author: Eddie Shrivastava
In recent years, whilst working on a various Data-projects (Data Engineering, Data Analysis and Data Warehouse projects) as a DBA/Data Engineer, I noticed that whilst for handling large datasets it's better to use specific technologies such as: SSIS, PowerShell, T-SQL etc. however, for smaller datasets, Excel is sill handy (in 2021) for some quick data transformations. Considering this, I made use of good old Excel VBA / Macros, which turned out to be very useful and time saving, therefore, I would like to share some of my code snippets especially the ones that are generic in nature i.e. they can be re-used in diverse scenarios and can be useful for other Data professionals. I'm happy for my code to be re-used (without any attached strings of licences).
VBA Code Templates & Samples:
' Following code converts a number written as words/alphabets (e.g. One, Two) into digits as 1 to 9Function GetDigit(WordedNumber)
WordedNumber = WorksheetFunction.Proper(WordedNumber) 'important for consistent results.
Select Case WordedNumber
Case "One", "0ne": GetDigit = 1
Case "Two", "Tw0": GetDigit = 2
Case "Three": GetDigit = 3
Case "Four": GetDigit = 4
Case "Five": GetDigit = 5
Case "Six": GetDigit = 6
Case "Seven": GetDigit = 7
Case "Eight": GetDigit = 8
Case "Nine": GetDigit = 9
Case "Ten": GetDigit = 10
Case "Eleven": GetDigit = 11
Case "Twelve": GetDigit = 12
Case "Thirteen": GetDigit = 13
Case "Fourteen": GetDigit = 14
Case "Fifteen": GetDigit = 15
Case "Sixteen": GetDigit = 16
Case "Seventeen": GetDigit = 17
Case "Eighteen": GetDigit = 18
Case "Nineteen": GetDigit = 19
Case "Twenty": GetDigit = 20
Case Else: GetDigit = ""
End Select
End Function
' Following code converts digits as 1 to 9 into number written as words/alphabets.
Select Case Val(Digit)
Case 1: GetWordedNumbers = "One"
Case 2: GetWordedNumbers = "Two"
Case 3: GetWordedNumbers = "Three"
Case 4: GetWordedNumbers = "Four"
Case 5: GetWordedNumbers = "Five"
Case 6: GetWordedNumbers = "Six"
Case 7: GetWordedNumbers = "Seven"
Case 8: GetWordedNumbers = "Eight"
Case 9: GetWordedNumbers = "Nine"
Case 10: GetWordedNumbers = "Ten"
Case 11: GetWordedNumbers = "Eleven"
Case 12: GetWordedNumbers = "Twelve"
Case 13: GetWordedNumbers = "Thirteen"
Case 14: GetWordedNumbers = "Fourteen"
Case 15: GetWordedNumbers = "Fifteen"
Case 16: GetWordedNumbers = "Sixteen"
Case 17: GetWordedNumbers = "Seventeen"
Case 18: GetWordedNumbers = "Eighteen"
Case 19: GetWordedNumbers = "Nineteen"
Case 20: GetWordedNumbers = "Twenty"
Case Else: GetWordedNumbers = ""
End Select
End Function
Looping through Objects:
Looping through Workbooks / Excel Files:
Example-1:
'following code loops through workbooks
'following code loops through all OPEN workbooks then displays those book-names:
Sub LoopingThroughBooks()
For Each book In
Workbooks
Text = Text &
book.Name & vbNewLine
Next book
MsgBox Text
End Sub
Looping through Sheets:
Example-1:
'following code loops through worksheets in current workbook
then displays those sheet-names:
Sub LoopingThroughSheets()
For Each sheet In
Worksheets
Text = Text & sheet.Name
& vbNewLine
Next sheet
MsgBox Text
End Sub
Looping through Columns or Rows:
Example-0: looping
through Rows
'Following code loops through the 1st row to the last row:
Sub LoopingRows()
Dim LastRow As Long,
r As Long
'LastRow =
ActiveSheet.UsedRange.Rows.Count
LastRow = Cells(Rows.Count,
1).End(xlUp).Row
For r = 1 To LastRow
' Your code here
Next r
End Sub
Example-1:
'First find out the last row / column then simply do a
counter-loop:
Sub UsedRangeColsRows()
Dim LastRow As Long,
LastCol As Integer, c As Integer, r As Long
LastRow =
ActiveSheet.UsedRange.Rows(ActiveSheet.UsedRange.Rows.Count).Row
LastCol =
ActiveSheet.UsedRange.Columns(ActiveSheet.UsedRange.Columns.Count).Column
LastColCount =
ActiveSheet.UsedRange.Columns.Count
Debug.Print LastRow
Debug.Print LastCol
& " which is same as " & LastColCount
For c = 1 To LastCol
For r = 1 To
LastRow
If Cells(r,
c) = "MyCheck" Then
' Your
code here
End If
Next r
Next c
End Sub
Example-2:
'This code loops
through & hides all columns except column-headers written in the 'arr'
array.
Function IsInArray(stringToBeFound As String, arr As Variant)
As Boolean
IsInArray = Not IsError(Application.Match(stringToBeFound,
arr, 0))
End Function
Sub HideCols()
Dim arr As Variant 'arr must Variant or String type as Range()
& Split() return Sub-Strings (array).
Dim LastRow As Long,
LastCol As Integer, c As Integer, r As Long
arr = Split("1.8
Station Ground of Incident, 2.1 Time of call, 3.2 Property Type", ",
")
LastCol =
ActiveSheet.UsedRange.Columns.Count
For c = 1 To LastCol
'If Cells(1, c) =
"MyValueCheck" Then
If
(IsInArray(Cells(1, c), arr)) Then
Cells(1, c).EntireColumn.Hidden =
False
Else
Debug.Print
"Hiding: " & Cells(1, c).Value
Cells(1,
c).EntireColumn.Hidden = True
End If
Next c
End Sub
Example-3:
'This code loops
through & deletes all columns except column-headers written in the 'arr'
array.
Function IsInArray(stringToBeFound As String, arr As Variant) As
Boolean
IsInArray = Not
IsError(Application.Match(stringToBeFound, arr, 0))
End Function
Sub DeleteCols()
Dim arr As Variant 'arr must Variant or String type as Range()
& Split() return Sub-Strings (array).
Dim LastRow As Long,
LastCol As Integer, c As Integer, r As Long
arr = Split("1.1
FRS Incident Number, 1.8 Station Ground of Incident, 2.1 Time of call, 3.2
Property Type, 4.3a X-Coordinate, 4.3b Y-Coordinate, 5.15 Cause/motive,
Textual_Address, Fire Classification", ", ")
For c =
ActiveSheet.UsedRange.Columns.Count To 1 Step -1
If Not
(IsInArray(Cells(1, c), arr)) Then
Cells(1,
c).EntireColumn.Delete
End If
Next c
End Sub
Sorting Array Elements in VBA
VBA has no built-in
support for sorting the values
stored in an array. One method that can be used to sort arrays is to put the data back on to a worksheet, sort the
data on the worksheet, and then read back the values from the worksheet into
the array. The second method for sorting arrays is to use any simple sort algorithm (such as Bubble sorting) to sort the
array in place.
Method-1: Sorting
by writing on Worksheet - sorting data then - read back into array:
Sub SortViaWorksheet()
Dim arr() As String '
this is the array to be sorted
Dim WS As Worksheet '
temporary worksheet
Dim R As Range
Dim N As Long
' populating
array
arr = Split("zzz,
aaa, nnn, ppp, bbb", ", ")
'Debug.Print
Join(arr, "; ")
Application.ScreenUpdating = False
' create a new sheet
Set WS =
ThisWorkbook.Worksheets.Add
' put the array
values on the worksheet
Set R =
WS.Range("A1").Resize(UBound(arr) - LBound(arr) + 1, 1)
R =
Application.Transpose(arr)
' sort the range
R.Sort key1:=R,
order1:=xlAscending, MatchCase:=False
' load the worksheet
values back into the array
'Erase arr 'Erase purges all elements & resizes
array to 0, hence ReDim/Split() will be required.
For N = 1 To
R.Rows.Count
arr(N - 1) = R(N,
1) 'N-1 because index starts with 0.
Next N
' delete the temporary sheet
Application.DisplayAlerts = False
WS.Delete
Application.DisplayAlerts = True
Application.ScreenUpdating = True
'
test/debug/confirmation
MsgBox Join(arr,
", ")
For N = LBound(arr)
To UBound(arr)
Debug.Print
arr(N)
Next N
End Sub
Method-2: Sorting
using manual sort: (bubble sort or quick sort)
Bubble sort
implementation:
'Two ways to call following BubbleSort() sub:
' BubbleSort
yourArrayName or
' Call
BubbleSort(yourArrayName)
Sub BubbleSort(arr)
Dim strTemp As String
Dim i As Long
Dim j As Long
Dim lngMin As Long:
lngMin = LBound(arr)
Dim lngMax As Long:
lngMax = UBound(arr) -1
For i = lngMin To
lngMax
For j = i + 1 To
lngMax
If arr(i) > arr(j)
Then 'for Descending order: If arr(i) < arr(j) Then
strTemp = arr(i)
arr(i) = arr(j)
arr(j) = strTemp
End If
Next j
Next i
End Sub
Quick sort
implementation:
Sub QuickSort(arr, Lo As Long, Hi As Long)
Dim varPivot As Variant
Dim varTmp As Variant
Dim tmpLow As Long
Dim tmpHi As Long
tmpLow = Lo
tmpHi = Hi
varPivot = arr((Lo +
Hi) \ 2)
Do While tmpLow <=
tmpHi
Do While arr(tmpLow)
< varPivot And tmpLow < Hi
tmpLow = tmpLow + 1
Loop
Do While varPivot
< arr(tmpHi) And tmpHi > Lo
tmpHi = tmpHi - 1
Loop
If tmpLow <= tmpHi
Then
varTmp =
arr(tmpLow)
arr(tmpLow) =
arr(tmpHi)
arr(tmpHi) = varTmp
tmpLow = tmpLow + 1
tmpHi = tmpHi - 1
End If
Loop
If Lo < tmpHi Then
QuickSort arr, Lo, tmpHi
If tmpLow < Hi Then
QuickSort arr, tmpLow, Hi
End Sub
Use above like this:
QuickSort MyArray, LBound(MyArray), UBound(MyArray)
or
Call QuickSort(MyArray, LBound(MyArray), UBound(MyArray))
Another Bubble sort example (for both Ascending &
Descending orders):
Option Explicit
Sub SortArray()
Dim MyArray As Variant, i As Long
MyArray = Array(1, 4, 2, 32, 5, 21)
' True being sort as Ascending. False will sort Descending.
MyArray = BubbleSrt(MyArray, True)
For i = LBound(MyArray) To UBound(MyArray)
MsgBox
MyArray(i)
Next i
End Sub
Public Function BubbleSrt(ArrayIn, Ascending As
Boolean)
' True being sort as Ascending. False will sort Descending.
Dim SrtTemp As Variant
Dim i As Long
Dim j As Long
If Ascending = True Then
For i =
LBound(ArrayIn) To UBound(ArrayIn)
For j = i
+ 1 To UBound(ArrayIn)
If
ArrayIn(i) > ArrayIn(j) Then
SrtTemp = ArrayIn(j)
ArrayIn(j) = ArrayIn(i)
ArrayIn(i) = SrtTemp
End If
Next j
Next i
Else
For i =
LBound(ArrayIn) To UBound(ArrayIn)
For j = i
+ 1 To UBound(ArrayIn)
If
ArrayIn(i) < ArrayIn(j) Then
SrtTemp = ArrayIn(j)
ArrayIn(j) = ArrayIn(i)
ArrayIn(i) = SrtTemp
End If
Next j
Next i
End If
BubbleSrt = ArrayIn
End Function
Deleting empty
columns
Example-1:
This
code deletes all empty columns in ActiveSheet (only):
Sub DeleteEmptyColumns()
'Application.ScreenUpdating = False
'Application can be omitted as object assumed in VBE
ScreenUpdating =
False
'Debug.Print
ActiveSheet.UsedRange.Columns.Count
For i =
ActiveSheet.UsedRange.Columns.Count To 1 Step -1
'Debug.Print
WorksheetFunction.CountA(Columns(i))
If
WorksheetFunction.CountA(Columns(i)) = 0 Then
Columns(i).Delete
End If
Next
ScreenUpdating =
True
End Sub
Example-2:
'This
code loops through all sheets and deletes any empty columns:
Sub LoopingThroughSheets()
Dim Text As String
ScreenUpdating =
False
For Each Sheet In
Worksheets
Text = Text
& Sheet.Name & vbNewLine
Sheet.Select
For i =
ActiveSheet.UsedRange.Columns.Count To 1 Step -1
'Debug.Print WorksheetFunction.CountA(Columns(i))
If
WorksheetFunction.CountA(Columns(i)) = 0 Then
Columns(i).Delete
End If
Next i
Next Sheet
ScreenUpdating =
True
MsgBox Text
End Sub
Counting a word in a column:
Sub CountBonfire()
Dim lRow As Long 'last row (of table)
Dim str1, str2 As
String 'strings to search
Dim cnt As Integer:
cnt = 0 'count number of findings
lRow =
ActiveSheet.Range("GP" &
ActiveSheet.Rows.Count).End(xlUp).Row
'MsgBox lRow
str1 =
"BONFIRE"
str2 = UCase("Controlled
burning")
'str3 =
UCase("")
For Each cell In
Range("AM1:AM" & lRow)
'Set wcf =
Range("d1").Find(cell.Value)
'Above is ok if
exact value matches in range - Not for <= or >= comparisons.
If
(InStr(UCase(cell.Value), str1)) <> 0 Or InStr(UCase(cell.Value), str2)
<> 0 Then
'MsgBox
cell.Row '_ Exit For
'MsgBox
ActiveSheet.Range("GN" & cell.Row).Value
'If
(InStr(UCase(ActiveSheet.Range("GN" & cell.Row).Value),
UCase("overborder"))) = 0 Then
'Exit For
cnt = cnt
+ 1
'End If
End If
Next cell
MsgBox cnt
End Sub
Finding a date in a range of dates:
Following VBA code takes the cursor (as Goto command) to
the current week (Assumption: Worksheet contains a column listing weekly-dates
(here A1:A300)).
Note: One of the easiest way to compare dates in VBA is
to convert them into long values using CLng() function, see used in the example
below.
Sub curr_week()
Dim cell As Range, _
wcf As Range
Dim today As
Long 'must compare dates in Long &
Date+Time or just Time in Double otherwise problematic.
today =
CLng(Date) 'converting current date
into Long.
For Each cell In
Range("A1:A300")
'Set wcf =
Range("d1").Find(cell.Value)
'Above is ok if
exact value matches in range - Not for <= or >= comparisons.
If
IsDate(cell.Value) And Not IsEmpty(cell.Value) Then
If today
>= CLng(cell.Value) And today < CLng(cell.Offset(1, 0).Value) Then
Set wcf =
cell
End If
End If
Next cell
If Not wcf Is Nothing
Then
Debug.Print
wcf.Value 'Ctrl+G to open Immediate
window where Debug-stmts print.
Application.Goto
wcf, True 'Application.Goto requires a
range.
Else
MsgBox "Date
range not found, Eddie to check Macro."
End If
End Sub
Changing cell's background colour-shade to highlighting certain data (used
in Data Analysis):
Method - 1: using Excel's Conditional Formatting
-> Data Bars / Icon Sets OR Colour
Scales.
Colouring a range -
using Interior.Color property:
Range("A1").Interior.Color
= RGB(255, 0, 0) 'using RGB function
Range("A1").Interior.ColorIndex =
37 'using ColorIndex
Setting
background color to 'No Fill':
Range("A1").Interior.ColorIndex = 0
Displaying
ColorIndex
MsgBox Selection.Interior.ColorIndex
Method - 2: using VBA Code.
Sub HighlihgtCellsAsPerValue()
Dim maxval As
Integer, rng As Range, cell As Range
Set rng =
Range("e4:g16") 'SET is
mandatory as we are assigning a reference (not literal value/LET).
For Each cell In rng
cell.Value =
WorksheetFunction.RandBetween(100, 999) 'filling cells with random values for
test.
Next
maxval =
WorksheetFunction.Max(rng)
'Debug.Print ((9 /
255) * maxval)
For Each cell In rng
'cell.Interior.Color = RGB(255, 255 - (cell.Value * 255 / maxval), 255 -
(cell.Value * 255 / maxval))
'above makes colours too dark, even though logically more correct.
cell.Interior.Color = RGB(255, 255 - (cell.Value * 80 / maxval), 255 -
(cell.Value * 80 / maxval))
Next
End Sub
Searching & highlighting matching values from two ranges (using Array from
a Range):
Ver-1 Requires:
two ranges in column B, file: ArrayFromRange_RowHide.xlsm.
Function IsInArray(stringToBeFound
As String, arr As Variant) As Boolean
IsInArray = Not
IsError(Application.Match(stringToBeFound, arr, 0))
End Function
Sub FindMatchingValsFromOtherRange()
Dim rng, cell As Range:
Set rng = Range("b1:b39")
Dim arr() As Variant 'arr must Variant or String type as Range()
& Split() return Sub-Strings (array).
'arr() =
Split("Essex, Berkshire, Dorset, Durham", ", ") 'manual
array - without using range.
arr = Range("B44:B56")
'Note: Range() always returns 2-D array (even if data is in single row/col).
'MsgBox arr(0, 0)
'Error: Subscript out of range as index in VBA starts with 1 (not 0 Ggrrr).
'Application.SendKeys
"^g ^g ^a {DEL} {F7}" 'Clearing
Immediate/Debug-output window.
Debug.Print Now
'Debug.Print
Join(arr, ", ")
For Each cell In rng
If
(IsInArray(cell.Value, arr)) Then
cell.Interior.Color = vbRed
'Highlight cell.
'cell.EntireRow.Hidden = True 'Hide EntireRow.
End If
Next cell
End Sub
Searching & hiding all rows except matching values from a set of values
(stored in an Array):
Ver-2 Requires
alteration in code below: define a range (38 cells) in column B, code then asks
user to continue to hide rows. file: "CIPFA Stats 2014-15.xlsm".
Function IsInArray(stringToBeFound
As String, arr As Variant) As Boolean
IsInArray = Not
IsError(Application.Match(stringToBeFound, arr, 0))
End Function
Sub HideOtherGroups()
Dim rngAllStns, cell
As Range
Dim totArrElems As
Integer: totArrElems = 0
Dim arr As Variant
Set rngAllStns =
Range("b6:b43")
rngAllStns.Select
ContiExec =
MsgBox("Are you happy with selected cells, continue hiding row?",
vbOKCancel + vbQuestion)
If ContiExec =
vbCancel Then 'incorrect selection of cells - exit sub
Exit Sub
End If
arr =
Split("Bedfordshire,Berkshire,Buckinghamshire,Cambridgeshire,Dorset,Durham,East
Sussex,Norfolk,Northamptonshire,Oxfordshire,Suffolk,West
Sussex,Wiltshire", ",") 'manual array - without using range.
'MsgBox Join(arr,
", ") & vbCrLf & vbCrLf & vbCrLf & "Total:
" & UBound(arr) + 1
'arr =
Range("B44:B56") 'Note: Range() always returns 2-D array (even if
data is in single row/col).
'MsgBox arr(0, 0)
'Error: Subscript out of range as index in VBA starts with 1 (not 0 Ggrrr).
'Application.SendKeys
"^g ^g ^a {DEL} {F7}" 'Clears
Immediate/Debug-output window.
Debug.Print Now
'Debug.Print Join(arr,
", ")
rngAllStns.EntireRow.Hidden = True
For Each cell In
rngAllStns
If
(IsInArray(cell.Value, arr)) Then
cell.EntireRow.Hidden = False
totArrElems =
totArrElems + 1
End If
Next cell
MsgBox "Total
count of visible rows: " & totArrElems & vbCrLf & vbCrLf &
"Out of total: " & rngAllStns.Rows.Count
End Sub
Searching & hiding all rows except matching values from a set of values
(stored in an Array):
Ver-3 Requires:
searches for "Avon" in column B, if found, auto-selects next 37
cells, then asks user to continue to hide rows, also now limited (500) row
search, uses Goto() instead of Cells().
File: "CIPFA Stats 2014-15.xlsm".
Function IsInArray(stringToBeFound As String, arr As Variant) As
Boolean
IsInArray = Not
IsError(Application.Match(stringToBeFound, arr, 0))
End Function
Sub HideOtherGroups()
'Search & hide all rows except matching values from a set of
values (stored in an Array):
'Ver-3 Requires: searches for "Avon" in column B, if
found, auto-selects next 37 cells, then asks user to continue to hide rows.,
file: "CIPFA Stats 2014-15.xlsm".
Dim rngAllStns, cell
As Range
Dim totArrElems As
Integer: totArrElems = 0
Dim arr As Variant 'arr must Variant or String type as Range()
& Split() return Sub-Strings (array).
'Set rngAllStns = Range("b6:b43") 'reading source-data from sheet/range
(instead of VBA code/array)
'Cells(1,
2).Select 'OR ActiveSheet.Cells(1, 2).Select 'doesn't
always activate/display the selection - unlike App.Goto()
Application.GoTo
ActiveSheet.Range("B1"), True
'MsgBox
ActiveCell.Address
Do While
ActiveCell.Row < 500
ActiveCell.Offset(1, 0).Select
If
(ActiveCell.Value = "Avon") Then
Set rngAllStns
= Range(ActiveCell, ActiveCell.Offset(37, 0))
'Offset doesn't count current cell.
Exit Do
End If
Loop
rngAllStns.Select
ContiExec =
MsgBox("Are you happy with selected cells/rows: " &
rngAllStns.Address & " -
Continue hiding row?", vbYesNo + vbQuestion)
If ContiExec = vbNo
Then 'incorrect selection of cells - exit sub
Exit Sub
End If
'Manually storing
stations in array - not reading from range on sheet:
arr =
Split("Bedfordshire,Berkshire,Buckinghamshire,Cambridgeshire,Dorset,Durham,East
Sussex,Norfolk,Northamptonshire,Oxfordshire,Suffolk,West
Sussex,Wiltshire", ",")
'MsgBox Join(arr,
", ") & vbCrLf & vbCrLf & vbCrLf & "Total:
" & UBound(arr) + 1
'arr =
Range("B44:B56") 'Note: Range() always returns 2-D array (even if
data is in single row/col).
'MsgBox arr(0, 0) 'Error:
Subscript out of range as index in VBA starts with 1 (not 0 Ggrrr).
'Application.SendKeys
"^g ^g ^a {DEL} {F7}" 'Clears
Immediate/Debug-output window.
Debug.Print Now
'Debug.Print Join(arr,
", ")
rngAllStns.EntireRow.Hidden = True
For Each cell In rngAllStns
If
(IsInArray(cell.Value, arr)) Then
cell.EntireRow.Hidden = False
totArrElems =
totArrElems + 1
End If
Next cell
MsgBox "Total
count of visible rows: " & totArrElems & vbCrLf & vbCrLf &
"Out of total: " & rngAllStns.Rows.Count
End Sub
Searching & hiding all rows except matching values from a set of
values (stored in an Array):
Ver-4 Requires:
searches for value set for variable 'FirstNameInRange' (such
as"Avon") in column B, if not found exits gracefully, if found,
auto-selects next 37 cells, then asks user to continue to hide rows, (search
limited to 500 rows), uses Goto() instead of Cells(). Hides only unwanted rows
(instead of hiding all rows first then unhide required). File: "CIPFA Stats 2014-15.xlsm".
Function IsInArray(stringToBeFound As String, arr As Variant)
As Boolean
IsInArray = Not
IsError(Application.Match(stringToBeFound, arr, 0))
End Function
Sub HideOtherGroups()
Dim rngAllStns, cell
As Range
Dim FirstNameInRange
As String: FirstNameInRange = "Avon"
Dim ContiExec,
visibleElems As Integer: visibleElems = 0
Dim arr As Variant
'Set rngAllStns =
Range("b6:b43") 'reading
source-data from sheet/range (instead of VBA code/array)
'Cells(1, 2).Select 'OR
ActiveSheet.Cells(1, 2).Select 'doesn't always activate/display the
selection - unlike App.Goto()
Application.Goto
ActiveSheet.Range("B1"), True
'MsgBox
ActiveCell.Address
Do While
ActiveCell.Row < 500
ActiveCell.Offset(1, 0).Select
If
(ActiveCell.Value = FirstNameInRange) Then
Set
rngAllStns = Range(ActiveCell, ActiveCell.Offset(46, 0)) 'total-1: Offset doesn't count current cell.
Exit Do
End If
Loop
'If Not rngAllStns Is
Nothing Then 'range not defined remains
EMPTY (not null), hence check with isEmpty().
If Not
IsEmpty(rngAllStns) Then
rngAllStns.Select
Else
MsgBox
"""" & FirstNameInRange & """"
& " not found."
Application.Goto
ActiveSheet.Range("B1"), True
Exit Sub
End If
ContiExec =
MsgBox("Are you happy with selected cells/rows: " &
rngAllStns.Address & " -
Continue hiding row?", vbYesNo + vbQuestion)
If ContiExec = vbNo
Then 'incorrect selection of cells - exit sub
Exit Sub
End If
'Manually storing
stations in array - not reading from range on sheet:
arr =
Split("Bedfordshire,Berkshire,Buckinghamshire,Cambridgeshire,Dorset,Durham,East
Sussex,Norfolk,Northamptonshire,Oxfordshire,Suffolk,West Sussex,Wiltshire",
",")
'MsgBox Join(arr,
", ") & vbCrLf & vbCrLf & vbCrLf & "Total:
" & UBound(arr) + 1
'arr =
Range("B44:B56") 'Note: Range() always returns 2-D array (even if
data is in single row/col).
'MsgBox arr(0, 0)
'Error: Subscript out of range as index in VBA starts with 1 (not 0 Ggrrr).
'Application.SendKeys
"^g ^g ^a {DEL} {F7}" 'Clears
Immediate/Debug-output window.
Debug.Print Now
'Debug.Print
Join(arr, ", ")
'rngAllStns.EntireRow.Hidden = True
'hiding all rows in the range.
For Each cell In
rngAllStns
If
(IsInArray(cell.Value, arr)) Then
'cell.EntireRow.Hidden = False
visibleElems
= visibleElems + 1
Debug.Print
visibleElems & ". " & cell.Value
Else
'.EntireRow.Delete
'when deleting rows from a range, run loop-steps from bottom to top, otherwise
error: "row/object doesn't exist".
cell.EntireRow.Hidden = True
End If
Next cell
MsgBox "Total
count of visible rows: " & visibleElems & vbCrLf & vbCrLf
& "Out of total: " & rngAllStns.Rows.Count
End Sub
Searching & deleting
rows not containing certain values (stored in an Array):
Requires: searches for value set for variable
'FirstNameInRange' (such as"Avon") in column B, if not found exits
gracefully, if found, auto-selects next 37 cells, then asks user to continue to
delete rows (search limited to 500 rows), uses Goto() instead of Cells().
Deletes rows from bottom (row-by-row).
File: "CIPFA Stats 2014-15.xlsm".
Function IsInArray(stringToBeFound As String, arr As Variant)
As Boolean
IsInArray = Not
IsError(Application.Match(stringToBeFound, arr, 0))
End Function
Sub DeleteRowsFromOtherGroups()
'Search & hide all rows except matching values from a set
of values (stored in an Array):
'Ver-3 Requires: searches for "Avon" in column B, if
found, auto-selects next 37 cells, then asks user to continue to hide rows.,
file: "CIPFA Stats 2014-15.xlsm".
Dim rngAllStns As
Range
Dim Firstrow, Lastrow
As Long
Dim FirstNameInRange
As String: FirstNameInRange = "England"
Dim ContiExec,
visibleElems As Integer: visibleElems = 0
Dim arr As Variant
'Set rngAllStns =
Range("b6:b43") 'reading
source-data from sheet/range (instead of VBA code/array)
'Cells(1,
2).Select 'OR ActiveSheet.Cells(1, 2).Select 'doesn't always
activate/display the selection - unlike App.Goto()
Application.Goto
ActiveSheet.Range("B19"), True
'MsgBox
ActiveCell.Address
Do While
ActiveCell.Row < 500
ActiveCell.Offset(1, 0).Select
If (ActiveCell.Value = FirstNameInRange)
Then
Set
rngAllStns = Range(ActiveCell, ActiveCell.Offset(46, 0)) 'total-1: Offset doesn't count current cell.
Exit Do
End If
Loop
'If Not rngAllStns Is
Nothing Then 'range not defined remains EMPTY (not null),
hence check with isEmpty().
If Not
IsEmpty(rngAllStns) Then
rngAllStns.Select
Firstrow =
rngAllStns.Cells(1).Row
Lastrow =
rngAllStns.Rows(rngAllStns.Rows.Count).Row
'MsgBox Firstrow
& " - " & Lastrow
Else
MsgBox
"""" & FirstNameInRange & """"
& " not found."
Exit Sub
End If
ContiExec =
MsgBox("Are you happy with selected cells/rows: " &
rngAllStns.Address & " -
Continue hiding row?", vbYesNo + vbQuestion)
If ContiExec = vbNo
Then 'incorrect selection of cells - exit sub
Exit Sub
End If
arr =
Split("Bedfordshire & Luton,Royal
Berkshire,Buckinghamshire,Cambridgeshire,Dorset,County Durham &
Darlington,East Sussex,Norfolk,Northamptonshire,Oxfordshire,Suffolk,West
Sussex,Wiltshire", ",") 'manual array - without using range.
'MsgBox Join(arr,
", ") & vbCrLf & vbCrLf & vbCrLf & "Total:
" & UBound(arr) + 1
'arr =
Range("B44:B56") 'Note: Range() always returns 2-D array (even if
data is in single row/col).
'MsgBox arr(0, 0)
'Error: Subscript out of range as index in VBA starts with 1 (not 0 Ggrrr).
'Application.SendKeys
"^g ^g ^a {DEL} {F7}" 'Clears
Immediate/Debug-output window.
Debug.Print Now
'Debug.Print Join(arr,
", ")
'rngAllStns.EntireRow.Hidden = True
'For Each cell In
rngAllStns
For Lrow = Lastrow To
Firstrow Step -1
With Cells(Lrow,
"B")
If
(IsInArray(.Value, arr)) Then
'cell.EntireRow.Hidden = False
visibleElems
= visibleElems + 1
Debug.Print
visibleElems & ". " & .Value
Else
.EntireRow.Delete 'when deleting
rows from a range, run loop-steps from bottom to top, otherwise error:
"row/object doesn't exist".
'.EntireRow.Hidden
= True
End If
End With
Next Lrow
MsgBox "Total
count of visible rows: " & visibleElems & vbCrLf & vbCrLf
& "Out of total: " & rngAllStns.Rows.Count
End Sub
Simple Cut-Copy-Paste using VBA:
Following
1 liner copies a Range within same Sheet (to a specified destination):
Range("A1:D4").Copy(Range("E5"))
Following
1 liner copies a Range from Sheet1 to Sheet2 (at specified destination):
Worksheets("Sheet1").Range("A1:D4").Copy
destination:=Worksheets("Sheet2").Range("E5")
Following
code is generated by Macro recording that copies 1st row from Sheet1 to Sheet2:
Sub CopyRow() Worksheets("Sheet1").Rows(1).Copy Worksheets("Sheet2").Select Worksheets("Sheet2").Rows(1).Select Worksheets("Sheet2").PasteEnd Sub
Following
code is hand written to skip blanks (Skip-Blanks including empty strings
""). Following checks for empty strings then copies a range:
Sub LoopingRows() Dim NewRow, LastRow As Long, r As Long: NewRow = 1 Sheets("Sheet3").Select LastRow = Cells(Rows.Count, 1).End(xlUp).Row For r = 1 To LastRow 'If Cells(r, 1) > 0 Then Debug.Print Len(Cells(r, 1)) If Len(Cells(r, 1)) > 0 Then Range("A" & r & ":B" & r).Copy Range("D" & NewRow) NewRow = NewRow + 1 End If' If r > 500 Then' Exit For' End If Next rEnd Sub
'Following code asks confirmation, if user clicks
Yes, Cuts the current row, and Pastes the row into specified sheet (here: "Completed
Inc.").
Sub MoveRowFromIncompleteToCompleteSheet()
sh = ActiveSheet.Name
r = ActiveCell.Row
ans =
MsgBox("Would you like to move current row to the ""Completed
Inc."" sheet?", vbYesNo, "Move Row!")
If ans = vbYes Then
ActiveCell.EntireRow.Cut _
Destination:=Sheets("Completed Inc.").Cells(Rows.Count,
1).End(xlUp).Offset(1, 0)
MsgBox "Row
number: " & r & " has been moved to ""Completed
Inc."" sheet"
Sheets(sh).Rows(r).EntireRow.Delete
End If
End Sub
Tirnary Operator in VBA
There is no ternary operator (
x = <condition> ? 1 : 2 ) in VBA, however if
you really would like to write ternary operator you can use the IIF() ternary consional
function in VBA. E.g.:
IIf(expr,
truepart, falsepart)
function,
function,
x =
IIf(<condition>, 1, 2), which reduces the verbosity of code.----------------------------------------------------------------------------------------------
Word VBA Samples:
VBA Code 1: Remove all hyperlinks of current document
Sub RemoveHyperlinks()
With ThisDocument
'Loop while there are hyperlinks on document
While .Hyperlinks.Count > 0
.Hyperlinks(1).Delete
Wend
End With
'Turn off auto-hyperlinking:
Application.Options.AutoFormatAsYouTypeReplaceHyperlinks =
False
End Sub
VAB code 2: Remove all hyperlinks in all open documents
Sub
RemoveHyperlinksInAllOpenDocuments()
' Removes all hyperlinks from any open documents
Dim doc As Document
Dim szOpenDocName As String
'Loop through all open documents:
For Each doc In Application.Documents
'Store the document name
szOpenDocName = doc.Name
'Remove the hyperlinks from that document
With Documents(szOpenDocName)
'Loop while there are hyperlinks afoot!
While .Hyperlinks.Count > 0
.Hyperlinks(1).Delete
Wend
End With
'Turn off auto-hyperlinking:
Application.Options.AutoFormatAsYouTypeReplaceHyperlinks
= False
Next doc
End Sub
File Handling (Word-VBA)
Private Sub WriteToFile_Click()
'Open file in
file-system: (creates new file if doesn't exist)
Open
"C:\Temp\HoldPastes.txt" For Append As #1
'Read Clipboard
content:
Dim myData As
DataObject
Dim strClip As String
Set myData = New
DataObject
myData.GetFromClipboard
strClip =
myData.GetText
'Paste Clipboard
content in the same file:
Selection.EndKey
Unit:=wdStory, Extend:=wdMove
Selection.PasteSpecial DataType:=wdPasteText
'Write to file:
Write #1,
"------------------------" & vbCrLf & vbCrLf & vbCrLf
Write #1, strClip
'Close file:
Close #1
End Sub
Private Sub ReadWriteFile_Click()
Dim sFirst, sLast,
sAddress, sCity, sState, sZip As String
' CREATE DATA FILE
' Open file in current
directory:
Open
"Datafile.txt" For Output As #1
' Write data to file.
Write #1,
"John", "Doe", "An Address", "A City",
"A State", "A Zip"
' Close File.
Close #1
' RETRIEVE DATA FROM
FILE
' Open file for input.
Open
"Datafile.txt" For Input As #1
' Loop until the end
of file is reached.
Do While Not EOF(1)
' Read data into
variables.
Input #1, sFirst,
sLast, sAddress, sCity, sState, sZip
' Print data to
Debug window.
Debug.Print sFirst,
sLast, sAddress, sCity, sState, sZip
Loop
' Close file.
Close #1
End Sub
Replacing Dodgy Characters
Converts Text-Paragraphs Into Html-Lists
Converts Text-Paragraphs Into Html-Lists
Sub ConvertIntoHtmlParas()
'This macro first
deletes all blank lines then encloses all the paragraphs in a document with
<p></p> (except paragraphs enclosed within <li></li>),
'Secondly this also
replaces all special (dodgy) characters with plain characters.
Dim pCount As Integer
Dim paraCount As
Integer
pCount =
ActiveDocument.Paragraphs.Count
Selection.HomeKey Unit:=wdStory
For x = 1 To (pCount
+ 1)
Selection.MoveEnd
Unit:=wdParagraph, Count:=1
If
Len(Trim(Replace(Selection.Text, vbCr, ""))) < 1 Then ' Note: in MS-Word a new-line character is
vbCr (NOT vbCrLf).
Selection.Delete
End If
Selection.EndOf
(wdParagraph)
Next
Selection.EndKey
Unit:=wdStory
Selection.TypeText
(vbCrLf)
Selection.HomeKey
Unit:=wdStory
paraCount =
ActiveDocument.Paragraphs.Count
For x = 2 To
paraCount 'Starting from 2 (instead of 1) to skip last blank paragraph that was
manually entered for consistency.
Selection.StartOf
(wdParagraph)
Selection.MoveEnd
Unit:=wdParagraph, Count:=1
If (InStr(1,
Trim(Selection.Text), "<li>", vbTextCompare) = 0) Then
Selection.StartOf (wdParagraph)
Selection.TypeText ("<p>")
Selection.EndOf (wdParagraph)
Selection.MoveLeft Unit:=wdCharacter, Count:=1
Selection.TypeText ("</p>")
Selection.MoveDown Unit:=wdLine,
Count:=1
Else
Selection.MoveRight Unit:=wdCharacter, Count:=1, Extend:=wdMove
End If
Next
'Following section replaces
dodgy characters: (¦`’‘“”– " ")
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text =
"¦"
.Replacement.Text
= "|"
.Wrap =
wdFindContinue
.Execute
Replace:=wdReplaceAll
.Text =
"‘" '
tr.Text.Replace(ChrW(8216), Chr(39))
.Replacement.Text
= "'"
.Wrap =
wdFindContinue
.Execute
Replace:=wdReplaceAll
.Text =
"’" '
tr.Text.Replace(ChrW(8217), Chr(39))
.Replacement.Text
= "'"
.Wrap =
wdFindContinue
.Execute Replace:=wdReplaceAll
.Text =
ChrW(8220)
.Replacement.Text
= """"
.Wrap =
wdFindContinue
.Execute
Replace:=wdReplaceAll
.Text =
ChrW(8221)
.Replacement.Text
= """"
.Wrap =
wdFindContinue
.Execute
Replace:=wdReplaceAll
.Text =
"–"
.Replacement.Text
= "-"
.Wrap =
wdFindContinue
.Execute
Replace:=wdReplaceAll
End With
End Sub
'Following Word-VBA Sub Converts Text-Paragraphs Into Html-Lists
Sub convertIntoHtmlLists()
'This macro encloses
selected paragraphs within <li></li>, deleting all blank lines.
Dim pCount As Integer
pCount =
ActiveDocument.Paragraphs.count
Selection.EndKey
Unit:=wdStory
Selection.TypeText
(vbCrLf)
Selection.HomeKey
Unit:=wdStory
For x = 1 To pCount
Selection.MoveEnd
Unit:=wdParagraph, count:=1
If
Len(Trim(Selection.Text)) <= 1 Then
'If (str = vbCr Or
str = vbCr Or str = vbCrLf) Then MsgBox
"It is CRLF" End If
Selection.Delete
Else
Selection.StartOf
(wdParagraph)
Selection.TypeText
("<li>")
Selection.EndOf
(wdParagraph)
Selection.MoveLeft
Unit:=wdCharacter, count:=1
Selection.TypeText
("</li>")
Selection.MoveRight
Unit:=wdCharacter, count:=1
End If
Next
End Sub
Sub convertIntoHtmlParas_OLD()
'This macro encloses
all the paragraphs in a document with <p></p> (In new separate
lines), deleting all blank lines,
'it excludes paragraphs
enclosed within <li></li> see next script.
Dim pCount As Integer
pCount =
ActiveDocument.Paragraphs.count
Selection.EndKey
unit:=wdStory
Selection.TypeText
(vbCrLf)
Selection.HomeKey
unit:=wdStory
For x = 1 To pCount
Selection.MoveEnd
unit:=wdParagraph, count:=1
If
Len(Trim(Selection.Text)) <= 1 Then
Selection.Delete
ElseIf (InStr(1,
Trim(Selection.Text), "<li>", vbTextCompare) <= 0) Then
Selection.StartOf
(wdParagraph)
Selection.TypeText
("<p>")
Selection.TypeText
(vbCrLf)
Selection.MoveDown
unit:=wdLine, count:=1
Selection.EndOf
(wdParagraph)
Selection.TypeText
("</p>")
Selection.TypeText
(vbCrLf)
Else
Selection.MoveRight
unit:=wdCharacter, count:=1, Extend:=wdMove
End If
Next
End Sub
ProperCase by macro
Sub Caps1stChar()
'This will convert
the case of the first letter of selection into uppercase.
Dim txt As String
txt = ""
For i = 1 To
Selection.Words.Count
txt = txt &
StrConv(Selection.Words(i), vbProperCase)
Next i
Selection.Text = txt
End Sub
ProperCase with 1st character bold (Ver-1):
e.g. Propercase
With 1st Character Bold:
'Ver1: Doesn't excludes anything, applies to all text
including: punctuations, numbers and words such as 'of' & 'for'.
Sub BoldCaps1stChar()
Dim cnt As Integer
cnt =
Selection.Words.Count
'move (without
wdExtend) cursor to the left/beginning of the selection:
Selection.MoveLeft
Unit:=wdCharacter, Count:=1
'running a loop to
select the 1st character then highlight it:
For i = 1 To cnt
Selection.MoveRight Unit:=wdCharacter, Count:=1, Extend:=wdExtend
Selection.Font.Bold = wdToggle
Selection.Range.Case = wdUpperCase
Selection.MoveRight Unit:=wdWord, Count:=1
Next i
End Sub
ProperCase with 1st character bold (Ver-2):
'Ver2: Excludes all non-alphabets
Sub BoldCaps1stAlphas()
Dim cnt As Integer:
cnt = Selection.Words.Count
Dim regEx As New
RegExp
'requires: VBA
reference to "Microsoft VBScript Regular Expressions 5.5" (under: VBE
-> Tools -> References)
regEx.Pattern =
"[a-zA-Z]"
'move (without
wdExtend) cursor to the left/beginning of the selection:
Selection.MoveLeft
Unit:=wdCharacter, Count:=1
'running a loop to
select the 1st character then highlight it:
For i = 1 To cnt
Selection.MoveRight Unit:=wdCharacter, Count:=1, Extend:=wdExtend
'MsgBox
Left(Selection, 1)
'If (Selection =
"," Or Selection = Chr(34) Or Selection = (Chr(34) &
",")) Then GoTo EndLoop End If
If
regEx.Test(Left(Selection, 1)) Then
Selection.Font.Bold = wdToggle
Selection.Range.Case = wdUpperCase
End If
Selection.MoveRight Unit:=wdWord, Count:=1
'EndLoop:
Next i
End Sub
ProperCase with 1st character bold (Ver-3):
'Ver3: (EdChoice) Excludes all non-alphabets & certain
words such as 'and', 'of' & 'for'.
Sub BoldCaps1stAlphas()
Dim cnt As Integer: cnt =
Selection.Words.Count
Dim regEx As New
RegExp
regEx.Pattern =
"[a-zA-Z]"
'move (without
wdExtend) cursor to the left/beginning of the selection:
Selection.MoveLeft
Unit:=wdCharacter, Count:=1
'running a loop to
select the 1st character then highlight it:
'For Each w In
Selection.Words 'this won't work here as
we need to alter the Words in selection.
For i = 1 To cnt
Selection.MoveRight Unit:=wdWord, Count:=1, Extend:=wdExtend 'select single word
'MsgBox
(Selection) 'check which word
'If (Selection =
"of " Or Selection = "for " Or Selection = (Chr(34) &
","))
'postfix-whitespace is essential i.e. "of " instead of
"of" otherwise offline/often etc. will be omitted.
If (InStr(Selection, "of ")
> 0 Or InStr(Selection, "for ") > 0 Or InStr(Selection, "and
") > 0) Then
Selection.MoveRight Unit:=wdWord, Count:=1
GoTo
NextIteration
End If
Selection.MoveLeft Unit:=wdWord, Count:=1
Selection.MoveRight Unit:=wdCharacter, Count:=1, Extend:=wdExtend
'check if
selected character is an alphabet:
If
(regEx.Test(Selection)) Then
Selection.Font.Bold = wdToggle
Selection.Range.Case = wdUpperCase
End If
Selection.MoveRight Unit:=wdWord, Count:=1
NextIteration:
Next i
End Sub
ProperCase with 1st character bold (Ver-4):
'Ver4: (EdChoice) Excludes all non-alphabets & certain
words (stored in Array elements - makes long if statement redundant.
Sub Highlight1stCharacter()
Dim cnt As Integer:
cnt = Selection.Words.Count
Dim regEx As New
RegExp
regEx.Pattern =
"[a-zA-Z]" 'requires:
VBE>Tools>Reference: "Microsoft VBScript Regular Expressions
5.5".
Dim excl() As String
excl() =
Split("and, of, for, to, with, in, on, a, an, the", ",
") 'One-liner array assignment.
'move (without
wdExtend) cursor to the left/beginning of the selection:
Selection.MoveLeft
Unit:=wdCharacter, Count:=1
'running a loop to
select the 1st character then highlight it:
'For Each w In
Selection.Words 'this won't work here as
we need to alter the Words in selection.
For i = 1 To cnt
Selection.MoveRight Unit:=wdWord, Count:=1, Extend:=wdExtend 'select single word
'MsgBox
(Selection) 'check which word
'If (Selection =
"of " Or Selection = "for " Or Selection = (Chr(34) &
","))
'postfix-whitespace is essential i.e. "of " instead of
"of" otherwise offline/often etc. will be omitted.
For Each x In excl
If
(InStr(Selection, x & " ") > 0) Then
Selection.MoveRight Unit:=wdWord, Count:=1
GoTo
NextIteration
End If
Next x
'above
array-iteration replaces need of following Loooooong if statement:
'If
(InStr(Selection, "of ") > 0 Or InStr(Selection, "for ")
> 0 Or InStr(Selection, "and ") > 0 Or InStr(Selection,
"to ") > 0) Then
Selection.MoveLeft Unit:=wdWord, Count:=1
Selection.MoveRight
Unit:=wdCharacter, Count:=1, Extend:=wdExtend
'check if
selected character is an alphabet:
If
(regEx.Test(Selection)) Then
Selection.Font.Bold = wdToggle
Selection.Range.Case = wdUpperCase
End If
Selection.MoveRight Unit:=wdWord, Count:=1
NextIteration:
Next i
End Sub
------------ x -------------
List of my IT blogs:
- Java Exam Notes
- Coding Tips & Techniques
- MySQL Data-types Summary
- HTML Tips & Tricks
- Office Apps Tips & Techniques
- VBA Useful Code Samples
- Useful VBA utilities for Microsoft Word