Sorting Arrays in VBA
Sorting Arrays in VBA
NET Programming
www.cpearson.com [email protected] XML Development
-->
Introduction
The VBA language has no support for sorting the values stored in an array. One method that can be used to sort
arrays is to put the data on to a worksheet, sort the data on the worksheet, and then read back the values from
the worksheet into the array. The other method for sorting arrays is to use the QSort algorithm to sort the array in
place. This page describes both methods, with variations on the QSort method.
Sub SortViaWorksheet()
Dim Arr(1 To 5) As String ' this is the array to be sorted
Dim WS As Worksheet ' temporary worksheet
Dim R As Range
Dim N As Long
Application.ScreenUpdating = False
' test/debug/confirmation
For N = LBound(Arr) To UBound(Arr)
Debug.Print Arr(N)
Next N
End Sub
The SortViaWorksheet function works if you are using Excel and the structure of the workbook is not
protected. It the workbook is protected, you'll get an error when creating the new sheet, so you will have to have
a scratch sheet in place beforehand or use an unused region of an existing (and unprotected) worksheet. Due to
these limitations, coupled with the fact that VBA is used in many applications other than Excel, it may be
desirable to employ a VBA-only method that doesn't rely on any outside objects.
Sorting by any method is an expensive operations, especially with large arrays, due to the number of swaps
made during the sorting process. Before sorting a large array, it might be useful to test whether the array is
already in sorted order and thus does not need to be sorted. Procedures for testing if an array is sorted can be
found on the IsArraySorted page.
InputArray is the array to be sorted. LB is the first element of the input array to sort. A value of -1 indicates to
start sorting with the first element. UB is the last element of the input array to sort. A value of -1 indicates to sort
to the end of the array. By modifying the values of LB and UB, you can sort only a subset of the array.
Descending, if False or omitted, causes the sort to progress in ascending order. If Descending is True, the
array is sorted in descending order. CompareMode indicates whether the sorting is case sensitive or case
insensitive. NoAlerts if True, supresses error messages that may occur. The function returns True if the sort
was successful or False if an error occurred. There are several procedures that support the QSortInPlace
function, so you should import the entire module into your project.
The Code
The code for QSortInPlace and supporting procedures is shown below:
'''''''''''''''''''''''''
' Set the default result.
'''''''''''''''''''''''''
QSortInPlace = False
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' This variable is used to determine the level
' of recursion (the function calling itself).
' RecursionLevel is incremented when this procedure
' is called, either initially by a calling procedure
' or recursively by itself. The variable is decremented
' when the procedure exits. We do the input parameter
' validation only when RecursionLevel is 1 (when
' the function is called by another function, not
' when it is called recursively).
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Static RecursionLevel As Long
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Keep track of the recursion level -- that is, how many
' times the procedure has called itself.
' Carry out the validation routines only when this
' procedure is first called. Don't run the
' validations on a recursive call to the
' procedure.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
RecursionLevel = RecursionLevel + 1
If RecursionLevel = 1 Then
''''''''''''''''''''''''''''''''''
' Ensure InputArray is an array.
''''''''''''''''''''''''''''''''''
If IsArray(InputArray) = False Then
If NoAlerts = False Then
MsgBox "The InputArray parameter is not an array."
End If
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
' InputArray is not an array. Exit with a False result.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
RecursionLevel = RecursionLevel - 1
Exit Function
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Test LB and UB. If < 0 then set to LBound and UBound
' of the InputArray.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If LB < 0 Then
LB = LBound(InputArray)
End If
If UB < 0 Then
UB = UBound(InputArray)
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''
' ensure the UB parameter is valid.
''''''''''''''''''''''''''''''''''''''''''''''''''''
Select Case UB
Case Is > UBound(InputArray)
If NoAlerts = False Then
MsgBox "The UB upper bound parameter is greater than the upper bound of the InputArray."
End If
RecursionLevel = RecursionLevel - 1
Exit Function
Case Is < LBound(InputArray)
If NoAlerts = False Then
MsgBox "The UB upper bound parameter is less than the lower bound of the InputArray."
End If
RecursionLevel = RecursionLevel - 1
Exit Function
Case Is < LB
If NoAlerts = False Then
MsgBox "the UB upper bound parameter is less than the LB lower bound parameter."
End If
RecursionLevel = RecursionLevel - 1
Exit Function
End Select
''''''''''''''''''''''''''''''''''''''''''''''''''''''
' if UB = LB, we have nothing to sort, so get out.
''''''''''''''''''''''''''''''''''''''''''''''''''''''
If UB = LB Then
QSortInPlace = True
RecursionLevel = RecursionLevel - 1
Exit Function
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Ensure that CompareMode is either vbBinaryCompare or
' vbTextCompare. If it is neither, default to vbTextCompare.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If (CompareMode = vbBinaryCompare) Or (CompareMode = vbTextCompare) Then
pCompareMode = CompareMode
Else
pCompareMode = vbTextCompare
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Begin the actual sorting process.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
CurLow = LB
CurHigh = UB
If LB = 0 Then
CurMidpoint = ((LB + UB) \ 2) + 1
Else
CurMidpoint = (LB + UB) \ 2 ' note integer division (\) here
End If
Temp = InputArray(CurMidpoint)
'''''''''''''''''''''''''''''''''''''
' If Descending is True, reverse the
' order of the array, but only if the
' recursion level is 1.
'''''''''''''''''''''''''''''''''''''
If Descending = True Then
If RecursionLevel = 1 Then
ReverseArrayInPlace2 InputArray, LB, UB
End If
End If
RecursionLevel = RecursionLevel - 1
QSortInPlace = True
End Function
End Function
NumberOfArrayDimensions = Ndx - 1
End Function
''''''''''''''''''''''''''''''''
' Set the default return value.
''''''''''''''''''''''''''''''''
ReverseArrayInPlace = False
'''''''''''''''''''''''''''''''''
' Ensure we have an array
'''''''''''''''''''''''''''''''''
If IsArray(InputArray) = False Then
If NoAlerts = False Then
MsgBox "The InputArray parameter is not an array."
End If
Exit Function
End If
''''''''''''''''''''''''''''''''''''''
' Test the number of dimensions of the
' InputArray. If 0, we have an empty,
' unallocated array. Get out with
' an error message. If greater than
' one, we have a multi-dimensional
' array, which is not allowed. Only
' an allocated 1-dimensional array is
' allowed.
''''''''''''''''''''''''''''''''''''''
Select Case NumberOfArrayDimensions(InputArray)
Case 0
'''''''''''''''''''''''''''''''''''''''''''
' Zero dimensions indicates an unallocated
' dynamic array.
'''''''''''''''''''''''''''''''''''''''''''
If NoAlerts = False Then
MsgBox "The input array is an empty, unallocated array."
End If
Exit Function
Case 1
'''''''''''''''''''''''''''''''''''''''''''
' We can reverse ONLY a single dimensional
' arrray.
'''''''''''''''''''''''''''''''''''''''''''
Case Else
'''''''''''''''''''''''''''''''''''''''''''
' We can reverse ONLY a single dimensional
' arrray.
'''''''''''''''''''''''''''''''''''''''''''
If NoAlerts = False Then
MsgBox "The input array multi-dimensional. ReverseArrayInPlace works only " & _
"on single-dimensional arrays."
End If
Exit Function
End Select
'''''''''''''''''''''''''''''''''''''''''''''
' Ensure that we have only simple data types,
' not an array of objects or arrays.
'''''''''''''''''''''''''''''''''''''''''''''
If IsSimpleDataType(InputArray(LBound(InputArray))) = False Then
If NoAlerts = False Then
MsgBox "The input array contains arrays, objects, or other complex data types." & vbCrLf & _
"ReverseArrayInPlace can reverse only arrays of simple data types."
Exit Function
End If
End If
ReverseArrayInPlace = True
End Function
''''''''''''''''''''''''''''''''
' Set the default return value.
''''''''''''''''''''''''''''''''
ReverseArrayInPlace2 = False
'''''''''''''''''''''''''''''''''
' Ensure we have an array
'''''''''''''''''''''''''''''''''
If IsArray(InputArray) = False Then
If NoAlerts = False Then
MsgBox "The InputArray parameter is not an array."
End If
Exit Function
End If
''''''''''''''''''''''''''''''''''''''
' Test the number of dimensions of the
' InputArray. If 0, we have an empty,
' unallocated array. Get out with
' an error message. If greater than
' one, we have a multi-dimensional
' array, which is not allowed. Only
' an allocated 1-dimensional array is
' allowed.
''''''''''''''''''''''''''''''''''''''
Select Case NumberOfArrayDimensions(InputArray)
Case 0
'''''''''''''''''''''''''''''''''''''''''''
' Zero dimensions indicates an unallocated
' dynamic array.
'''''''''''''''''''''''''''''''''''''''''''
If NoAlerts = False Then
MsgBox "The input array is an empty, unallocated array."
End If
Exit Function
Case 1
'''''''''''''''''''''''''''''''''''''''''''
' We can reverse ONLY a single dimensional
' arrray.
'''''''''''''''''''''''''''''''''''''''''''
Case Else
'''''''''''''''''''''''''''''''''''''''''''
' We can reverse ONLY a single dimensional
' arrray.
'''''''''''''''''''''''''''''''''''''''''''
If NoAlerts = False Then
MsgBox "The input array multi-dimensional. ReverseArrayInPlace works only " & _
"on single-dimensional arrays."
End If
Exit Function
End Select
'''''''''''''''''''''''''''''''''''''''''''''
' Ensure that we have only simple data types,
' not an array of objects or arrays.
'''''''''''''''''''''''''''''''''''''''''''''
If IsSimpleDataType(InputArray(LBound(InputArray))) = False Then
If NoAlerts = False Then
MsgBox "The input array contains arrays, objects, or other complex data types." & vbCrLf & _
"ReverseArrayInPlace can reverse only arrays of simple data types."
Exit Function
End If
End If
If LB < 0 Then
LB = LBound(InputArray)
End If
If UB < 0 Then
UB = UBound(InputArray)
End If
ReverseArrayInPlace2 = True
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Test if V is an array. We can't just use VarType(V) = vbArray
' because the VarType of an array is vbArray + VarType(type
' of array element). E.g, the VarType of an Array of Longs is
' 8195 = vbArray + vbLong.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If IsArray(V) = True Then
IsSimpleDataType = False
Exit Function
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' We must also explicitly check whether V is an object, rather
' relying on VarType(V) to equal vbObject. The reason is that
' if V is an object and that object has a default proprety, VarType
' returns the data type of the default property. For example, if
' V is an Excel.Range object pointing to cell A1, and A1 contains
' 12345, VarType(V) would return vbDouble, the since Value is
' the default property of an Excel.Range object and the default
' numeric type of Value in Excel is Double. Thus, in order to
' prevent this type of behavior with default properties, we test
' IsObject(V) to see if V is an object.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If IsObject(V) = True Then
IsSimpleDataType = False
Exit Function
End If
'''''''''''''''''''''''''''''''''''''
' Test the value returned by VarType.
'''''''''''''''''''''''''''''''''''''
Select Case VarType(V)
Case vbArray, vbDataObject, vbObject, vbUserDefinedType
'''''''''''''''''''''''
' not simple data types
'''''''''''''''''''''''
IsSimpleDataType = False
Case Else
''''''''''''''''''''''''''''''''''''
' otherwise it is a simple data type
''''''''''''''''''''''''''''''''''''
IsSimpleDataType = True
End Select
End Function
Dim N As Long
'''''''''''''''''''''''''''''''''''''''''''''''''''
' If Arr is not an array, return FALSE and get out.
'''''''''''''''''''''''''''''''''''''''''''''''''''
If IsArray(Arr) = False Then
IsArrayAllocated = False
Exit Function
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Try to get the UBound of the array. If the array has not been allocated,
' an error will occur. Test Err.Number to see if an error occured.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
N = UBound(Arr, 1)
If Err.Number = 0 Then
'''''''''''''''''''''''''''''''''''''
' No error. Array has been allocated.
'''''''''''''''''''''''''''''''''''''
IsArrayAllocated = True
Else
'''''''''''''''''''''''''''''''''''''
' Error. Unallocated array.
'''''''''''''''''''''''''''''''''''''
IsArrayAllocated = False
End If
End Function
You can download the file with all the example code on this page.
The world's choice for creating NET-based Commercial Quality Add-Ins for Office
Add-In Express Is The Most Important Tool For Creating Commerical Level Components
Learn more about Excel and VBA (Visual Basic for Applications).
Cite this page as:
Source: www.cpearson.com/excel/SortingArrays.aspx Copyright 2018, Charles H. Pearson Citation Information
This site created with Microsoft Visual Studio 2013 Premium and ASP.NET 4