37

Does anyone know how to sort a collection in VBA?

stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
Alex Gordon
  • 57,446
  • 287
  • 670
  • 1,062
  • 4
    First of all you should define what is in the collection and how you expect it to be sorted. Otherwise it is all just speculations. – Daniel Dušek Feb 07 '17 at 12:34

12 Answers12

46

Late to the game... here's an implementation of the MergeSort algorithm in VBA for both Arrays and Collections. I tested the performance of this implementation against the BubbleSort implementation in the accepted answer using randomly generated strings. The chart below summarizes the results, i.e. that you should not use BubbleSort to sort a VBA collection.

Performance Comparison

You can download the source code from my GitHub Repository or just copy/paste the source code below into the appropriate modules.

For a collection col, just call Collections.sort col.

Collections module

'Sorts the given collection using the Arrays.MergeSort algorithm.
' O(n log(n)) time
' O(n) space
Public Sub sort(col As collection, Optional ByRef c As IVariantComparator)
    Dim a() As Variant
    Dim b() As Variant
    a = Collections.ToArray(col)
    Arrays.sort a(), c
    Set col = Collections.FromArray(a())
End Sub

'Returns an array which exactly matches this collection.
' Note: This function is not safe for concurrent modification.
Public Function ToArray(col As collection) As Variant
    Dim a() As Variant
    ReDim a(0 To col.count)
    Dim i As Long
    For i = 0 To col.count - 1
        a(i) = col(i + 1)
    Next i
    ToArray = a()
End Function

'Returns a Collection which exactly matches the given Array
' Note: This function is not safe for concurrent modification.
Public Function FromArray(a() As Variant) As collection
    Dim col As collection
    Set col = New collection
    Dim element As Variant
    For Each element In a
        col.Add element
    Next element
    Set FromArray = col
End Function

Arrays module

    Option Compare Text
Option Explicit
Option Base 0

Private Const INSERTIONSORT_THRESHOLD As Long = 7

'Sorts the array using the MergeSort algorithm (follows the Java legacyMergesort algorithm
'O(n*log(n)) time; O(n) space
Public Sub sort(ByRef a() As Variant, Optional ByRef c As IVariantComparator)

    If c Is Nothing Then
        MergeSort copyOf(a), a, 0, length(a), 0, Factory.newNumericComparator
    Else
        MergeSort copyOf(a), a, 0, length(a), 0, c
    End If
End Sub


Private Sub MergeSort(ByRef src() As Variant, ByRef dest() As Variant, low As Long, high As Long, off As Long, ByRef c As IVariantComparator)
    Dim length As Long
    Dim destLow As Long
    Dim destHigh As Long
    Dim mid As Long
    Dim i As Long
    Dim p As Long
    Dim q As Long

    length = high - low

    ' insertion sort on small arrays
    If length < INSERTIONSORT_THRESHOLD Then
        i = low
        Dim j As Long
        Do While i < high
            j = i
            Do While True
                If (j <= low) Then
                    Exit Do
                End If
                If (c.compare(dest(j - 1), dest(j)) <= 0) Then
                    Exit Do
                End If
                swap dest, j, j - 1
                j = j - 1 'decrement j
            Loop
            i = i + 1 'increment i
        Loop
        Exit Sub
    End If

    'recursively sort halves of dest into src
    destLow = low
    destHigh = high
    low = low + off
    high = high + off
    mid = (low + high) / 2
    MergeSort dest, src, low, mid, -off, c
    MergeSort dest, src, mid, high, -off, c

    'if list is already sorted, we're done
    If c.compare(src(mid - 1), src(mid)) <= 0 Then
        copy src, low, dest, destLow, length - 1
        Exit Sub
    End If

    'merge sorted halves into dest
    i = destLow
    p = low
    q = mid
    Do While i < destHigh
        If (q >= high) Then
           dest(i) = src(p)
           p = p + 1
        Else
            'Otherwise, check if p<mid AND src(p) preceeds scr(q)
            'See description of following idom at: https://stackoverflow.com/a/3245183/3795219
            Select Case True
               Case p >= mid, c.compare(src(p), src(q)) > 0
                   dest(i) = src(q)
                   q = q + 1
               Case Else
                   dest(i) = src(p)
                   p = p + 1
            End Select
        End If

        i = i + 1
    Loop

End Sub

IVariantComparator class

Option Explicit

'The IVariantComparator provides a method, compare, that imposes a total ordering over a collection _
of variants. A class that implements IVariantComparator, called a Comparator, can be passed to the _
Arrays.sort and Collections.sort methods to precisely control the sort order of the elements.

'Compares two variants for their sort order. Returns -1 if v1 should be sorted ahead of v2; +1 if _
v2 should be sorted ahead of v1; and 0 if the two objects are of equal precedence. This function _
should exhibit several necessary behaviors: _
  1.) compare(x,y)=-(compare(y,x) for all x,y _
  2.) compare(x,y)>= 0 for all x,y _
  3.) compare(x,y)>=0 and compare(y,z)>=0 implies compare(x,z)>0 for all x,y,z
Public Function compare(ByRef v1 As Variant, ByRef v2 As Variant) As Long
End Function

If no IVariantComparator is provided to the sort methods, then the natural ordering is assumed. However, if you need to define a different sort order (e.g. reverse) or if you want to sort custom objects, you can implement the IVariantComparator interface. For example, to sort in reverse order, just create a class called CReverseComparator with the following code:

CReverseComparator class

Option Explicit

Implements IVariantComparator

Public Function IVariantComparator_compare(v1 As Variant, v2 As Variant) As Long
    IVariantComparator_compare = v2-v1
End Function

Then call the sort function as follows: Collections.sort col, New CReverseComparator

Bonus Material: For a visual comparison of the performance of different sorting algorithms check out https://www.toptal.com/developers/sorting-algorithms/

Community
  • 1
  • 1
Austin
  • 8,018
  • 2
  • 31
  • 37
  • 1
    This was hard for me in VBA, like it took a herculean effort to make work since I'm not really a programmer. I finally used cpearson's array sort because making a Factory would be easier with bricks and the Collections.ToArray function adds an annoying extra item because it redim a(0 to count) instead of redim a(0 to count-1) since my arrays start at 0 based and my collections start at 1. – Henrietta Martingale May 25 '18 at 14:15
  • 3
    Seems like great information and code. It is not real clear where it needs to be placed for someone unfamiliar with VBA. "Just copy/paste the source code below into the appropriate modules." Where are these modules? – Joe McGrath Aug 08 '18 at 13:16
  • 9
    There are a lot of functions in here that are not defined in the modules and are not standard VBA functions, e.g. `copyOf()`,`length()`,`swap()`. It isn't testable in its current form; was there meant to be another module included with the answer? – sigil Jan 08 '19 at 23:51
  • 3
    I can't even find these functions in the GitHub Repository. E.g. Arrays.copyOf is stated in the Readme file but not included in Arrays.bas. The code in VBA-Utilities.xlam doesn't compile either because of missing methods. – Jörg Brenninkmeyer Aug 27 '19 at 10:01
34

The code below from this post uses a bubble sort

Sub SortCollection()

    Dim cFruit As Collection
    Dim vItm As Variant
    Dim i As Long, j As Long
    Dim vTemp As Variant

    Set cFruit = New Collection

    'fill the collection
    cFruit.Add "Mango", "Mango"
    cFruit.Add "Apple", "Apple"
    cFruit.Add "Peach", "Peach"
    cFruit.Add "Kiwi", "Kiwi"
    cFruit.Add "Lime", "Lime"

    'Two loops to bubble sort
    For i = 1 To cFruit.Count - 1
        For j = i + 1 To cFruit.Count
            If cFruit(i) > cFruit(j) Then
                'store the lesser item
                vTemp = cFruit(j)
                'remove the lesser item
                cFruit.Remove j
                're-add the lesser item before the
                'greater Item
                cFruit.Add vTemp, vTemp, i
            End If
        Next j
    Next i

    'Test it
    For Each vItm In cFruit
        Debug.Print vItm
    Next vItm

End Sub
djv
  • 15,168
  • 7
  • 48
  • 72
Dick Kusleika
  • 32,673
  • 4
  • 52
  • 73
  • Thanks -- just needed to change vTemp to be of type Object to sort a collection of objects. – Ron Rosenfeld Oct 01 '14 at 19:56
  • 13
    Can we please not promote bubble sort. It is such a lousy algorithm. – Johan Aug 13 '15 at 12:17
  • 1
    You can skip the 'key' parameter, and just put in an extra comma I found out. – bmende Aug 23 '15 at 10:38
  • Also, if you try to shorten and put in cFruit.Remove cFruit(j) you get a run-time error – bmende Aug 23 '15 at 10:53
  • 1
    @Johan Agreed... I added an implementation of MergeSort below – Austin Jul 11 '16 at 03:34
  • First: I hate(!!!) VBA Second: I get a runtime error (438) on `vTemp = cFruit(j)` and cant figure out why. – Jan Dec 06 '17 at 09:32
  • `Set vTemp = cFruit(j)` helped me out, now I got stuck on `cFruit.Add vTemp, vTemp, i` with runtime error (13). .... VBA ..... >:( – Jan Dec 06 '17 at 09:37
  • Solution: `cFruit.Add vTemp, , i` @bmende I HAD to skip the key parameter, such a pain... – Jan Dec 06 '17 at 09:40
  • When you add an object (as opposed to a scalar value) you have to use the `Set` keyword as you discovered. The key to a collection has to be a string. When I'm adding strings, I can use the same thing for key and item. But when the item is an object, you have to use something different. Like if you were adding command buttons, you might use `vTemp.Name` as the key (or some other string property of the object). Certainly nothing wrong with using the index as you did, though. – Dick Kusleika Dec 06 '17 at 13:29
  • +1 for Bubble Sort, if your data is nearly ordered then bubble sort is the way to go. Check out this comparison: https://www.toptal.com/developers/sorting-algorithms – SlowLearner Jul 12 '18 at 00:11
28

You could use a ListView. Although it is a UI object, you can use its functionality. It supports sorting. You can store data in Listview.ListItems and then sort like this:

Dim lv As ListView
Set lv = New ListView

lv.ListItems.Add Text:="B"
lv.ListItems.Add Text:="A"

lv.SortKey = 0            ' sort based on each item's Text
lv.SortOrder = lvwAscending
lv.Sorted = True
MsgBox lv.ListItems(1)    ' returns "A"
MsgBox lv.ListItems(2)    ' returns "B"
cxw
  • 16,685
  • 2
  • 45
  • 81
ameisenmann
  • 339
  • 3
  • 2
  • 5
    This is sheer genius! I just tried it and it works very nicely. You can also sort on a particular subitem if you want to keep multiple sort orders in the same table. Don't forget to add the reference to `mscomctl.ocx`. – cxw Feb 06 '17 at 17:47
  • 2
    C:\Windows\SysWOW64\mscomctl.ocx Microsoft Common Controls . This is fab, surprised it can run without a form. – S Meaden Feb 07 '17 at 12:57
  • 1
    another workaround: copy collection to the range on the spreadsheet, sort the range and copy it back – ilya Dec 07 '20 at 06:07
  • 1
    This might work but is advised against. When working with a VBA collection and temporarily clone/copy it to a listview, make use of its sorting capabilities and then put the sorted items back into a collection (array) is cumbersome programming. Better use a generic sorting algorithm a mentionde in the answer of @Austin. Although this also involves a copy back-and-forth operatioin – Youp Bernoulli May 26 '22 at 17:17
12

Collection is a rather wrong object for sorting.

The very point of a collection is to provide very fast access to a certain element identified by a key. How the items are stored internally should be irrelevant.

You might want to consider using arrays instead of collections if you actually need sorting.


Other than that, yes, you can sort items in a collection.
You need to take any sorting algorithm available on the Internet (you can google inplementations in basically any language) and make a minor change where a swap occurs (other changes are unnecessary as vba collections, like arrays, can be accessed with indices). To swap two items in a collection, you need to remove them both from the collection and insert them back at the right positions (using the third or the forth parameter of the Add method).

GSerg
  • 76,472
  • 17
  • 159
  • 346
  • 1
    Using an array doesn't have the `.add` in vba for dynamic additions to the Array. – James Mertz Jun 29 '12 at 20:27
  • @KronoS I was talking about `Collection`. – GSerg Jun 29 '12 at 20:37
  • 1
    I understand, but you suggested using arrays instead of collections, which don't allow for dynamically adding to the array very easily. – James Mertz Jun 29 '12 at 20:42
  • @KronoS The first part of the answer, separated with a line, is rather not connected to the second part. For arrays, you don't need to add any items when sorting. – GSerg Jun 29 '12 at 20:49
  • There are some other problems with arrays in Excel, such as that you can use them as return values in functions. – Dynamicbyte Dec 17 '12 at 13:52
  • @Dynamicbyte Don't get that. You can return an array from a function, and you can return a collection. – GSerg Dec 17 '12 at 14:03
  • @GSerg: Sorry, I made a mistake it should be ".. you cannot use them as return values" – Dynamicbyte Jan 09 '13 at 12:22
  • @GSerg: You cannot return an array from a function directly. That is only possible by using a Variant as the return type. – Dynamicbyte Jan 30 '13 at 15:59
  • 1
    @Dynamicbyte Yes you can. `Function foo() As Long()` returns an array of `Long`s. You're probably thinking about VB5. – GSerg Jan 30 '13 at 17:38
  • Sorting the `ThisWorkbook.Worksheets` collection is very handy. – RubberDuck Jun 02 '14 at 12:54
  • I found that simply using an ArrayList instead of a collection was the best solution in my case. Even if you have to use a collection to start with for some reason, converting to an ArrayList and using .sort is probably still simpler than anything else. – SendETHToThisAddress Apr 15 '20 at 09:27
  • @technoman23 There is no ArrayList in VBA, and I don't think bringing the one from .Net is worth the overhead. – GSerg Apr 15 '20 at 09:56
8

There is no native sort for the Collection in VBA, but since you can access items in the collection via index, you can implement a sorting algorithm to go through the collection and sort into a new collection.

Here's a HeapSort algorithm implementation for VBA/VB 6.

Here's what appears to be a BubbleSort algorithm implementation for VBA/VB6.

Russ Cam
  • 124,184
  • 33
  • 204
  • 266
5

If your collection doesn't contain objects and you only need to sort ascending, you might find this easier to understand:

Sub Sort(ByVal C As Collection)
Dim I As Long, J As Long
For I = 1 To C.Count - 1
    For J = I + 1 To C.Count
        If C(I) > C(J) Then Swap C, I, J
    Next
Next
End Sub

'Take good care that J > I
Sub Swap(ByVal C As Collection, ByVal I As Long, ByVal J As Long)
C.Add C(J), , , I
C.Add C(I), , , J + 1
C.Remove I
C.Remove J
End Sub

I hacked this up in minutes, so this may not be the best bubble sort, but it should be easy to understand, and hence easy to modify for your own purposes.

4

This is my implementation of BubbleSort:

Public Function BubbleSort(ByRef colInput As Collection, _
                                    Optional asc = True) As Collection

    Dim temp                    As Variant
    Dim counterA                As Long
    Dim counterB                As Long

    For counterA = 1 To colInput.Count - 1
        For counterB = counterA + 1 To colInput.Count
            Select Case asc
            Case True:
                If colInput(counterA) > colInput(counterB) Then
                    temp = colInput(counterB)
                    colInput.Remove counterB
                    colInput.Add temp, temp, counterA
                End If

            Case False:
                If colInput(counterA) < colInput(counterB) Then
                    temp = colInput(counterB)
                    colInput.Remove counterB
                    colInput.Add temp, temp, counterA
                End If
            End Select
        Next counterB
    Next counterA

    Set BubbleSort = colInput

End Function

Public Sub TestMe()

    Dim myCollection    As New Collection
    Dim element         As Variant

    myCollection.Add "2342"
    myCollection.Add "vityata"
    myCollection.Add "na"
    myCollection.Add "baba"
    myCollection.Add "ti"
    myCollection.Add "hvarchiloto"
    myCollection.Add "stackoveflow"
    myCollection.Add "beta"
    myCollection.Add "zuzana"
    myCollection.Add "zuzan"
    myCollection.Add "2z"
    myCollection.Add "alpha"

    Set myCollection = BubbleSort(myCollection)

    For Each element In myCollection
        Debug.Print element
    Next element

    Debug.Print "--------------------"

    Set myCollection = BubbleSort(myCollection, False)

    For Each element In myCollection
        Debug.Print element
    Next element

End Sub

It takes the collection by reference, thus it can easily return it as a function and it has an optional parameter for Ascending and Descending sorting. The sorting returns this in the immediate window:

2342
2z
alpha
baba
beta
hvarchiloto
na
stackoveflow
ti
vityata
zuzan
zuzana
--------------------
zuzana
zuzan
vityata
ti
stackoveflow
na
hvarchiloto
beta
baba
alpha
2z
2342
Vityata
  • 42,633
  • 8
  • 55
  • 100
3

This code snippet works well, but it is in java.

To translate it you could do it like this:

 Function CollectionSort(ByRef oCollection As Collection) As Long
Dim smTempItem1 As SeriesManager, smTempItem2 As SeriesManager
Dim i As Integer, j As Integer
i = 1
j = 1

On Error GoTo ErrFailed
Dim swapped As Boolean
swapped = True
Do While (swapped)
    swapped = False
    j = j + 1

    For i = 1 To oCollection.Count - 1 - j
        Set smTempItem1 = oCollection.Item(i)
        Set smTempItem2 = oCollection.Item(i + 1)

        If smTempItem1.Diff > smTempItem2.Diff Then
            oCollection.Add smTempItem2, , i
            oCollection.Add smTempItem1, , i + 1

            oCollection.Remove i + 1
            oCollection.Remove i + 2

            swapped = True
        End If
    Next
Loop
Exit Function

ErrFailed:
     Debug.Print "Error with CollectionSort: " & Err.Description
     CollectionSort = Err.Number
     On Error GoTo 0
End Function

SeriesManager is just a class that stores the difference between two values. It can really be any number value you want to sort on. This by default sorts in ascending order.

I had difficulty sorting a collection in vba without making a custom class.

June7
  • 19,874
  • 8
  • 24
  • 34
1

This is a VBA implementation of the QuickSort algorithm, which is often a better alternative to MergeSort:

Public Sub QuickSortSortableObjects(colSortable As collection, Optional bSortAscending As Boolean = True, Optional iLow1, Optional iHigh1)
    Dim obj1 As Object
    Dim obj2 As Object
    Dim clsSortable As ISortableObject, clsSortable2 As ISortableObject
    Dim iLow2 As Long, iHigh2 As Long
    Dim vKey As Variant
    On Error GoTo PtrExit

    'If not provided, sort the entire collection
    If IsMissing(iLow1) Then iLow1 = 1
    If IsMissing(iHigh1) Then iHigh1 = colSortable.Count

    'Set new extremes to old extremes
    iLow2 = iLow1
    iHigh2 = iHigh1

    'Get the item in middle of new extremes
    Set clsSortable = colSortable.Item((iLow1 + iHigh1) \ 2)
    vKey = clsSortable.vSortKey

    'Loop for all the items in the collection between the extremes
    Do While iLow2 < iHigh2

        If bSortAscending Then
            'Find the first item that is greater than the mid-Contract item
            Set clsSortable = colSortable.Item(iLow2)
            Do While clsSortable.vSortKey < vKey And iLow2 < iHigh1
                iLow2 = iLow2 + 1
                Set clsSortable = colSortable.Item(iLow2)
            Loop

            'Find the last item that is less than the mid-Contract item
            Set clsSortable2 = colSortable.Item(iHigh2)
            Do While clsSortable2.vSortKey > vKey And iHigh2 > iLow1
                iHigh2 = iHigh2 - 1
                Set clsSortable2 = colSortable.Item(iHigh2)
            Loop
        Else
            'Find the first item that is less than the mid-Contract item
            Set clsSortable = colSortable.Item(iLow2)
            Do While clsSortable.vSortKey > vKey And iLow2 < iHigh1
                iLow2 = iLow2 + 1
                Set clsSortable = colSortable.Item(iLow2)
            Loop

            'Find the last item that is greater than the mid-Contract item
            Set clsSortable2 = colSortable.Item(iHigh2)
            Do While clsSortable2.vSortKey < vKey And iHigh2 > iLow1
                iHigh2 = iHigh2 - 1
                Set clsSortable2 = colSortable.Item(iHigh2)
            Loop
        End If

        'If the two items are in the wrong order, swap the rows
        If iLow2 < iHigh2 And clsSortable.vSortKey <> clsSortable2.vSortKey Then
            Set obj1 = colSortable.Item(iLow2)
            Set obj2 = colSortable.Item(iHigh2)
            colSortable.Remove iHigh2
            If iHigh2 <= colSortable.Count Then _
                colSortable.Add obj1, Before:=iHigh2 Else colSortable.Add obj1
            colSortable.Remove iLow2
            If iLow2 <= colSortable.Count Then _
                colSortable.Add obj2, Before:=iLow2 Else colSortable.Add obj2
        End If

        'If the Contracters are not together, advance to the next item
        If iLow2 <= iHigh2 Then
            iLow2 = iLow2 + 1
            iHigh2 = iHigh2 - 1
        End If
    Loop

    'Recurse to sort the lower half of the extremes
    If iHigh2 > iLow1 Then QuickSortSortableObjects colSortable, bSortAscending, iLow1, iHigh2

    'Recurse to sort the upper half of the extremes
    If iLow2 < iHigh1 Then QuickSortSortableObjects colSortable, bSortAscending, iLow2, iHigh1

PtrExit:
End Sub

The objects stored in the collection must implement the ISortableObject interface, which must be defined in your VBA project. To do that, add a class module called ISortableObject with the following code:

Public Property Get vSortKey() As Variant
End Property
igorsp7
  • 441
  • 2
  • 4
1

I want to go a little bit further with igorsp7 QuickSort

If you dont wan't to use special Interface, just for the sake of sorting you can use CallByName function:

Public Sub QuickSortCollection(colSortable As Object, nameOfSortingProperty As String, Optional bSortAscending As Boolean = True, Optional iLow1, Optional iHigh1)
Dim obj1 As Object
Dim obj2 As Object
Dim clsSortable As Object
Dim clsSortable2 As Object
Dim iLow2 As Long, iHigh2 As Long
Dim vKey As Variant
On Error GoTo PtrExit

'If not provided, sort the entire collection
If IsMissing(iLow1) Then iLow1 = 1
If IsMissing(iHigh1) Then iHigh1 = colSortable.Count

'Set new extremes to old extremes
iLow2 = iLow1
iHigh2 = iHigh1

'Get the item in middle of new extremes
Set clsSortable = colSortable.Item((iLow1 + iHigh1) \ 2)
vKey = CallByName(clsSortable, nameOfSortingProperty, VbGet)

'Loop for all the items in the collection between the extremes
Do While iLow2 < iHigh2

    If bSortAscending Then
        'Find the first item that is greater than the mid-Contract item
        Set clsSortable = colSortable.Item(iLow2)
        Do While CallByName(clsSortable, nameOfSortingProperty, VbGet) < vKey And iLow2 < iHigh1
            iLow2 = iLow2 + 1
            Set clsSortable = colSortable.Item(iLow2)
        Loop

        'Find the last item that is less than the mid-Contract item
        Set clsSortable2 = colSortable.Item(iHigh2)
        Do While CallByName(clsSortable2, nameOfSortingProperty, VbGet) > vKey And iHigh2 > iLow1
            iHigh2 = iHigh2 - 1
            Set clsSortable2 = colSortable.Item(iHigh2)
        Loop
    Else
        'Find the first item that is less than the mid-Contract item
        Set clsSortable = colSortable.Item(iLow2)
        Do While CallByName(clsSortable, nameOfSortingProperty, VbGet) > vKey And iLow2 < iHigh1
            iLow2 = iLow2 + 1
            Set clsSortable = colSortable.Item(iLow2)
        Loop

        'Find the last item that is greater than the mid-Contract item
        Set clsSortable2 = colSortable.Item(iHigh2)
        Do While CallByName(clsSortable2, nameOfSortingProperty, VbGet) < vKey And iHigh2 > iLow1
            iHigh2 = iHigh2 - 1
            Set clsSortable2 = colSortable.Item(iHigh2)
        Loop
    End If

    'If the two items are in the wrong order, swap the rows
    If iLow2 < iHigh2 And CallByName(clsSortable, nameOfSortingProperty, VbGet) <> CallByName(clsSortable2, nameOfSortingProperty, VbGet) Then
        Set obj1 = colSortable.Item(iLow2)
        Set obj2 = colSortable.Item(iHigh2)
        colSortable.Remove iHigh2
        If iHigh2 <= colSortable.Count Then _
            colSortable.Add obj1, before:=iHigh2 Else colSortable.Add obj1
        colSortable.Remove iLow2
        If iLow2 <= colSortable.Count Then _
            colSortable.Add obj2, before:=iLow2 Else colSortable.Add obj2
    End If

    'If the Contracters are not together, advance to the next item
    If iLow2 <= iHigh2 Then
        iLow2 = iLow2 + 1
        iHigh2 = iHigh2 - 1
    End If
Loop

'Recurse to sort the lower half of the extremes
If iHigh2 > iLow1 Then Call QuickSortCollection(colSortable, nameOfSortingProperty, bSortAscending, iLow1, iHigh2)

'Recurse to sort the upper half of the extremes
If iLow2 < iHigh1 Then Call QuickSortCollection(colSortable, nameOfSortingProperty, bSortAscending, iLow2, iHigh1)

PtrExit:
End Sub

Also i've changed colSortable to be Object, as I'm using a lot of custom typed collections.

lumenn
  • 11
  • 1
1

As mentioned, Collections do not have a built in sort feature. I came up with a simpler implementation using VBA Collection's built in After property.

This method loops through each existing item in the Collection, and once the new item (NewItem) comes later than the current loop value (Col.Item(i)) by ASCII comparison, it exits the loop and adds NewItem into that spot.

Private Sub InsertCollectionValueAlphabetically(Col As Collection, NewItem As String)

    Dim i As Long
    
    If Col.Count = 0 Then
        Col.Add NewItem, NewItem  'First value gets added without trying to loop through
        Exit Sub
    End If
    
    For i = 1 To Col.Count
        'Convert to lower case to get predictable behavior after ASCII text comparison
        If (LCase(NewItem) < LCase(Col.Item(i))) Then Exit For
    Next i
    
    If i = 1 Then
        Col.Add NewItem, NewItem, 1
    Else
        Col.Add NewItem, NewItem, , i - 1
    End If
End Sub
SandPiper
  • 2,816
  • 5
  • 30
  • 52
1

Added missing features( copyOf(), length(), swap() ) to the answer above(@Austin).

Public Function copyOf(a As Variant) As Variant()
    Dim el As Variant
    Dim ar() As Variant
    Dim i As Integer
    ReDim ar(UBound(a))
    i = 0
    For Each el In a
        If IsEmpty(el) Then
            Exit For
        End If
        Set ar(i) = el
        i = i + 1
    Next

    copyOf = ar
End Function
    
Public Function length(a As Variant) As Long
    length = UBound(a)
End Function

Public Sub swap(arr() As Variant, a As Integer, b As Integer)
    Dim x As Variant
    Set x = arr(a)
    Set arr(a) = arr(b)
    Set arr(b) = x
End Sub