logo
vbRad Home
Source Code
Book Reviews
Forum
Links
About Us
Contribute

Compare Databases with SQL Effects Clarity
 
 How to sort objects in a collection

Posted on
3/8/2002
Author:
Matthew Ferry
Email:
Not Shown
Applies To OS:
NT, 9x, 2000
Product:
6 only



Sorting objects in a collection is not necessarily as simple as you would hope - or at least having a routine that can sort any collection using any object is not that straightforward. Most programmers know how to sort and this piece is not about sorting algorythms, of which there are really quite a few if you look into it. Also, the code presented here is not really intended to sort large collections, as not much of an attempt has been made to optimize it for speed. But this code does show you how to handle the issues you will come across if you need generic code to sort objects that are contained in a VB Collection.

There are four main problems:

  • You can't just move items around in a collection (at least not easily)
  • Which property of the contained objects do you want to sort on?
  • How do you generically call that property?
  • How do you generically handle sorting on different data types?

Fortunately these are not too dificult to solve. Lets take the first issue - because its not really practical to move items around in a collection - the best approach is to simply make a new collection as you spin through the original collection. In fact this approach, coupled with the VB Collection's Add method optional parameters allow you to sort the data in essentially one pass.

The second and third issues can be handled with VB's CallByName function - probably the most under used function in VB. If you are into writing generic code - you should really look into this function as it provides some great flexibility in a lot of different situations. There are some caveats to be aware of if what you are calling can raise an error though - so look into the Knowledge Base about CallByName if you are interested. But here we won't deal with that since we are assuming that we are calling fairly safe code (in fact whatever property you are sorting the objects by had better return its value quickly - or your sort performance will really stink).

And the final issue presented above can easily be handled with a flag parameter to the function to indicate what kind of sort to do.

Lets look at the code.

Code below provides the following functionality:

  • Sort a collection of objects
  • Sort can be by any named property on the contained objects
  • Support for numeric or string sorts (date sorts could be added easily too)

Project Instructions
  1. Create a new standard project
  2. Add 1 listbox, and a button called 'btnSort'
  3. Set the Caption of the command button to Sort
  4. Create a class called 'clsTest.' This will be our object in the collection to test with.
  5. Create a module (bas file) called 'modItemSort.' This will hold the generic sorting routine.
  6. Cut and Paste the code below into the form, the class, and the module, as appropriate.
Add the following to the module
Option Explicit

Public Function SortItemCollection(col As Collection, strPropertyName, Optional blnCompareNumeric As Boolean = False) As Collection
    Dim colNew As Collection
    Dim objCurrent As Object
    Dim objCompare As Object
    Dim lngCompareIndex As Long
    Dim strCurrent As String
    Dim strCompare As String
    Dim blnGreaterValueFound As Boolean

    'make a copy of the collection, ripping through it one item
    'at a time, adding to new collection in right order...
    
    Set colNew = New Collection
    
    For Each objCurrent In col
    
        'get value of current item...
        strCurrent = CallByName(objCurrent, strPropertyName, VbGet)
        
        'setup for compare loop
        blnGreaterValueFound = False
        lngCompareIndex = 0
        
        For Each objCompare In colNew
            lngCompareIndex = lngCompareIndex + 1
            
            strCompare = CallByName(objCompare, strPropertyName, VbGet)
            
            'optimization - instead of doing this for every iteration, have 2 different loops...
            If blnCompareNumeric = True Then
                'this means we are looking for a numeric sort order...
                
                If Val(strCurrent) < Val(strCompare) Then
                    'found an item in compare collection that is greater...
                    'add it to the new collection...
                    blnGreaterValueFound = True
                    colNew.Add objCurrent, , lngCompareIndex
                    Exit For
                End If
                
            Else
                'this means we are looking for a string sort...
                
                If strCurrent < strCompare Then
                    'found an item in compare collection that is greater...
                    'add it to the new collection...
                    blnGreaterValueFound = True
                    colNew.Add objCurrent, , lngCompareIndex
                    Exit For
                End If
            
            End If
        Next
        
        'if we didn't find something bigger, just add it to the end of the new collection...
        If blnGreaterValueFound = False Then
            colNew.Add objCurrent
        End If
              
    Next

    'return the new collection...
    Set SortItemCollection = colNew
    Set colNew = Nothing

End Function

Add the following to the Class
Option Explicit

Public ID As Long
Public NameValue As String

Add the following to the Form
Option Explicit

Private col As Collection

Private Sub btnSort_Click()
    Dim obj As clsTest

    Set col = SortItemCollection(col, "ID", True)
    
    List1.Clear
    
    For Each obj In col
        List1.AddItem obj.NameValue
    Next

End Sub

Private Sub Form_Load()
    Dim obj As clsTest

    'build a collection of test objects when the form loads...
    
    Set col = New Collection
    
    Set obj = New clsTest
    obj.ID = 6
    obj.NameValue = "Item Six"
    col.Add obj
    
    Set obj = New clsTest
    obj.ID = 2
    obj.NameValue = "Item Two"
    col.Add obj
    
    Set obj = New clsTest
    obj.ID = 3
    obj.NameValue = "Item Three"
    col.Add obj
    
    Set obj = New clsTest
    obj.ID = 1
    obj.NameValue = "Item One"
    col.Add obj
    
    Set obj = New clsTest
    obj.ID = 4
    obj.NameValue = "Item Four"
    col.Add obj
    
    Set obj = New clsTest
    obj.ID = 5
    obj.NameValue = "Item Five"
    col.Add obj
    
    Set obj = New clsTest
    obj.ID = 7
    obj.NameValue = "Item Seven"
    col.Add obj
    
    
    For Each obj In col
        List1.AddItem obj.NameValue
    Next
    
End Sub
Remarks

Run the project and click the button - the items in the list box will be sorted because the collection used to populate the list box is sorted by the code in the module.



Add Your Comment  

Name: Email Address: all fields optional
Notify me via email when someone responds to this message (valid email required).

Enter the word:
 



Comments
#1. By Anonymous. Posted on 11/6/2007 8:56:11 PM
where is the callbyname function. My vb6 does not recognize it.

#2. By Anonymous. Posted on 11/7/2007 12:13:49 AM
VB6 certainly does recognize CallByName function.

#3. By Anonymous. Posted on 11/7/2007 3:47:22 PM
where is the callbyname function. My vb6 does not recognize it.

#4. By Aswin. Posted on 1/4/2008 12:22:51 PM
if one declares the strcompare and strcurrent as objects in stead of strings then the code does not have to implement different paths for different property types. In this case the callbyname function would return objects in stead of strings, the objects would be of type string, int32 etc and could be compared using a single statement.

#5. By Will. Posted on 4/21/2008 3:29:43 AM
You are a guru man! Thanks

#6. By Rob. Posted on 4/22/2008 8:26:48 PM
Really useful and saved me a fair bit of effort. Thanks a lot.