VBA map implementation - vba

I need good map class implementation in VBA.
This is my implementation for integer key
Box class:
Private key As Long 'Key, only positive digit
Private value As String 'Value, only
'Value getter
Public Function GetValue() As String
GetValue = value
End Function
'Value setter
Public Function setValue(pValue As String)
value = pValue
End Function
'Ket setter
Public Function setKey(pKey As Long)
Key = pKey
End Function
'Key getter
Public Function GetKey() As Long
GetKey = Key
End Function
Private Sub Class_Initialize()
End Sub
Private Sub Class_Terminate()
End Sub
Map class:
Private boxCollection As Collection
'Init
Private Sub Class_Initialize()
Set boxCollection = New Collection
End Sub
'Destroy
Private Sub Class_Terminate()
Set boxCollection = Nothing
End Sub
'Add element(Box) to collection
Public Function Add(Key As Long, value As String)
If (Key > 0) And (containsKey(Key) Is Nothing) Then
Dim aBox As New Box
With aBox
.setKey (Key)
.setValue (value)
End With
boxCollection.Add aBox
Else
MsgBox ("В словаре уже содержится элемент с ключем " + CStr(Key))
End If
End Function
'Get key by value or -1
Public Function GetKey(value As String) As Long
Dim gkBox As Box
Set gkBox = containsValue(value)
If gkBox Is Nothing Then
GetKey = -1
Else
GetKey = gkBox.GetKey
End If
End Function
'Get value by key or message
Public Function GetValue(Key As Long) As String
Dim gvBox As Box
Set gvBox = containsKey(Key)
If gvBox Is Nothing Then
MsgBox ("Key " + CStr(Key) + " dont exist")
Else
GetValue = gvBox.GetValue
End If
End Function
'Remove element from collection
Public Function Remove(Key As Long)
Dim index As Long
index = getIndex(Key)
If index > 0 Then
boxCollection.Remove (index)
End If
End Function
'Get count of element in collection
Public Function GetCount() As Long
GetCount = boxCollection.Count
End Function
'Get object by key
Private Function containsKey(Key As Long) As Box
If boxCollection.Count > 0 Then
Dim i As Long
For i = 1 To boxCollection.Count
Dim fBox As Box
Set fBox = boxCollection.Item(i)
If fBox.GetKey = Key Then Set containsKey = fBox
Next i
End If
End Function
'Get object by value
Private Function containsValue(value As String) As Box
If boxCollection.Count > 0 Then
Dim i As Long
For i = 1 To boxCollection.Count
Dim fBox As Box
Set fBox = boxCollection.Item(i)
If fBox.GetValue = value Then Set containsValue = fBox
Next i
End If
End Function
'Get element index by key
Private Function getIndex(Key As Long) As Long
getIndex = -1
If boxCollection.Count > 0 Then
For i = 1 To boxCollection.Count
Dim fBox As Box
Set fBox = boxCollection.Item(i)
If fBox.GetKey = Key Then getIndex = i
Next i
End If
End Function
All ok if i insert 1000 pairs key-value. But if 50000, a program freezes.
How i can solve this problem? Or maybe there more better solution?

Main problem with your implementation is that the operation containsKey is quite expensive (O(n) complex) and it is called at every insert and it never breaks even when it "knows" what would be the result.
This might help a little:
...
If fBox.GetKey = Key Then
Set containsKey = fBox
Exit Function
End If
...
In order to reduce the containsKey complexity typical things to do would be
keep the keys sorted so that you can use binary search instead of linear search
keep the keys in tree or in a hashed buckets
The most straightforward thing to do would be using the Collection's built-in (hopefully optimized) ability to store/retrieve items by a key.
Store:
...
boxCollection.Add Item := aBox, Key := CStr(Key)
...
Retrieve (not tested, based on this answer):
Private Function containsKey(Key As Long) As Box
On Error GoTo err
Set containsKey = boxCollection.Item(CStr(Key))
Exit Function
err:
Set containsKey = Nothing
End Function
See also:
MSDN: How to: Add, Delete, and Retrieve Items of a Collection (Visual Basic)
Stack Overflow: Does VBA have Dictionary Structure?
Newton Excel Bach: Arrays vs Collections vs Dictionary Objects (and Dictionary help)

Related

VBA Class Module - When to Stop and Where to declare public constant?

I have been coding vba for quite sometime now and it is only recently that I have begun diving into doing up some class module.
some questions on my mind is when should I stop including functions and properties in a class ?
i.e. I am creating a class module where by it record table properties (i.e. row of header column) I went on further to create a retrieve data function where it gets data from other table with the same class.
Question 1: am I over doing it? I tend to want to put all the function in one class.
Question 2: if I want to set all classes to have the same default initialization do I declare a public const in the class module itself or the normal modules?
Here is my class module :
Private pHeaderNames As Object
Private pHeaderRow As Long
Private pSubHeaderNames As Object
Private pSubHeaderRow As Long
Private pDataRowStart As Long
Private pInputColStart As Long
Private pTableColStart As Long
Private pHeaderLastCol As Long
Private pTblWorksheet As Worksheet
Private pFileURL As String
Private pFileName As String
'---------------------------------------------- Initialization
Private Sub Class_Initialize()
pHeaderRow = 1
pDataRowStart = 2
pTableColStart = 1
pInputColStart = pTableColStart
pSubHeaderRow = pHeaderRow
pHeaderLastCol = Cells(pHeaderRow, Columns.Count).End(xlToLeft).Column
Set pHeaderNames = CreateObject("Scripting.Dictionary")
Set pSubHeaderNames = CreateObject("Scripting.Dictionary")
End Sub
Public Property Get FileURL() As String
FileURL = pFileURL
End Property
Public Property Let FileURL(Value As String)
pFileURL = Value
End Property
Public Property Get FileName() As String
FileName = pFileName
End Property
Public Property Let FileName(Value As String)
pFileName = Value
End Property
Function OpenWorkbook(URL As String, Name As String)
pFileURL = URL
pFileName = Name
Call Workbook_open(pFileURL, pFileName)
End Function
Public Property Let SetSheet(Value As String)
If pFileName - "" Then
Set pTblWorksheet = ActiveWorkbook.Worksheets(Value)
Else
Set pTblWorksheet = Workbook(pFileName).Worksheets(Value)
End If
End Property
Public Property Get TblWorksheet() As Worksheet
TblWorksheet = pTblWorksheet
End Property
Public Property Let TblWorksheet(Sheet As Worksheet)
pTblWorksheet = Sheet
End Property
'---------------------------------------------- HeaderLastCol
Public Property Get HeaderLastCol() As Long
HeaderLastCol = pHeaderLastCol
End Property
Public Property Let HeaderLastCol(Value As Long)
pHeaderLastCol = Value
End Property
'---------------------------------------------- HeaderRow
Public Property Let HeaderRow(Value As Long)
pHeaderLastCol = Cells(pHeaderRow, Columns.Count).End(xlToLeft).Column
pHeaderRow = Value
End Property
Public Property Get HeaderRow() As Long
HeaderRow = pHeaderRow
End Property
'---------------------------------------------- SubHeaderRow
Public Property Let SubHeaderRow(Value As Long)
pSubHeaderRow = Value
End Property
Public Property Get SubHeaderRow() As Long
SubHeaderRow = pSubHeaderRow
End Property
'---------------------------------------------- InputColStart
Sub SetInputColStart(KEY As Variant)
pInputColStart = pHeaderNames(KEY)
End Sub
Public Property Get InputColStart() As Long
InputColStart = pInputColStart
End Property
Public Property Let InputColStart(Value As Long)
pInputColStart = Value
End Property
'---------------------------------------------- DataRowStart
Public Property Get DataRowStart() As Long
DataRowStart = pDataRowStart
End Property
Public Property Let DataRowStart(Value As Long)
pDataRowStart = Value
End Property
'---------------------------------------------- TableColStart
Public Property Get TableColStart() As Long
TableColStart = pTableColStart
End Property
Public Property Let TableColStart(Value As Long)
pTableColStart = Value
End Property
'---------------------------------------------- HeaderName
Sub GetHeaderNames()
With pHeaderNames
For i = pTableColStart To pHeaderLastCol
If Not .Exists(UCase(Cells(pHeaderRow, i).Value)) Then
.Add UCase(Cells(pHeaderRow, i).Value), i
End If
Next i
End With
End Sub
Function HeaderName(KEY As String)
If pHeaderNames.Exists(KEY) Then
HeaderName = pHeaderNames(KEY)
Else
HeaderName = ""
End If
End Function
Function CountHeaderNames()
CountHeaderNames = pHeaderNames.Count
End Function
Function PrintHeaderObject()
For Each KEY In pHeaderNames.keys
Debug.Print KEY, pHeaderNames(KEY)
Next
End Function
'---------------------------------------------- SubHeaderName
Sub GetSubHeaderNames()
With pSubHeaderNames
For i = pTableColStart To pHeaderLastCol
If Not .Exists(UCase(Cells(pSubHeaderRow, i).Value)) Then
.Add UCase(Cells(pSubHeaderRow, i).Value), i
End If
Next i
End With
End Sub
Function SubHeaderName(KEY As String)
If pSubHeaderNames.Exists(KEY) Then
SubHeaderName = pSubHeaderNames(KEY)
Else
SubHeaderName = "" 'or raise an error...
End If
End Function
Function CountSubHeaderNames()
CountSubHeaderNames = pSubHeaderNames.Count
End Function
Function PrintSubHeaderObject()
For Each KEY In pSubHeaderNames.keys
Debug.Print KEY, pSubHeaderNames(KEY)
Next
End Function
Function RetrieveData(FromSht As Worksheet, ByVal FromTable As cTable)
Dim KEY As String
'CurrentSht = ActiveSheet
For i = pTableColStart To pHeaderLastCol
KEY = Cells(pHeaderRow, i).Value
If FromTable.HeaderName(KEY) = "" Then
GoTo Nexti
Else
With FromSht
.Activate
rD_LastRow = 10
Set Source = .Range(.Cells(FromTable.DataRowStart, FromTable.HeaderName(KEY)), _
.Cells(rD_LastRow, FromTable.HeaderName(KEY)))
End With
With CurrentSht
.Activate
.Range(.Cells(DataRowStart, i), _
.Cells(DataRowStart, i)) _
.Resize(Source.Rows.Count, Source.Columns.Count).Cells.Value = Source.Cells.Value
End With
End If
Nexti:
Next i
End Function
Here is the module, so you can see that I have always need to declare headerRow , subHeaderRow and Datarowstart, even thou I have a default initialization set in the class module, is there away to change the default initialization based on the main module? or I just have to do it in the class module? (I trying to make the class portable)
Sub test()
Dim sht As Worksheet
Set wb = ActiveWorkbook
Set sht = wb.Sheets("Skin(Units)")
With Worksheets("Skin(Units)")
.Activate
Set SkinUnits = New cTable
Debug.Print TypeName(SkinUnits)
SkinUnits.HeaderRow = 1
SkinUnits.SubHeaderRow = 3
SkinUnits.DataRowStart = 4
SkinUnits.GetHeaderNames
SkinUnits.GetSubHeaderNames
SkinUnits.PrintHeaderObject
SkinUnits.PrintSubHeaderObject
SkinUnits.SetInputColStart ("Start")
End With
With Worksheets("Pain(Units)")
.Activate
Set PainUnits = New cTable
PainUnits.HeaderRow = 1
PainUnits.SubHeaderRow = 3
PainUnits.DataRowStart = 4
PainUnits.GetHeaderNames
PainUnits.GetSubHeaderNames
PainUnits.PrintHeaderObject
PainUnits.PrintSubHeaderObject
PainUnits.SetInputColStart ("Start")
Debug.Print PainUnits.HeaderName("SKU")
Debug.Print TypeName(sht), TypeName(SkinUnits)
Call test22222(SkinUnits)
Call PainUnits.RetrieveData(sht, SkinUnits)
End With
End Sub
Function test22222(ByVal X As cTable)
Debug.Print X.HeaderRow
End Function

Should I use two methods with String and Integer,or use TypeOf?

We have a series of collections of objects that all have two fields for sure, an integer "key" and a string "name". We have methods that return a particular instance based on the name or key...
Public ReadOnly Property Inflations(ByVal K as String) As InflationRow
' look for K in the names
End Property
Public ReadOnly Property Inflations(ByVal K as Integer) As InflationRow
' look for K in the keys
End Property
COM interop has the interesting side effect that only the first method with a given name is exported. So we added this...
Public ReadOnly Property Inflations(ByVal K as Object) As InflationRow
Return Inflations(K)
End Property
This leads to some confusion when reading the code, and multiple lines doing the same thing. So what if I replace all of this with...
Public ReadOnly Property Inflations(ByVal K as Object) As InflationRow
If TypeOf K Is String then
'do a string lookup on name
else
'try it on the key
end if
End Property
This does the same thing in the end, but seems much easier to read and keeps all the code in the same place. But...
Most of the calls into this code doesn't come from COM, but our own code. Will many calls to TypeOf in our .net code be significantly slower than allowing the runtime to make this decision through polymorphism? I really don't know enough about the runtime to even guess.
Test it and see! :-)
Option Strict On
Module Module1
Sub Main()
Dim irc As New InflationRowCollection
For i As Integer = 0 To 4999
irc.InflationList.Add(New InflationRow With {.IntProperty = i, .StrProperty = i.ToString})
Next i
Dim t1 As Date = Now
For i As Integer = 0 To 4999
Dim ir1 As InflationRow = irc.Inflations(i)
Dim ir2 As InflationRow = irc.Inflations(i.ToString)
Next i
Dim t2 As Date = Now
For i As Integer = 0 To 4999
Dim ir1 As InflationRow = irc.InflationsObj(i)
Dim ir2 As InflationRow = irc.InflationsObj(i.ToString)
Next i
Dim t3 As Date = Now
Console.WriteLine("Typed property: " & (t2 - t1).TotalSeconds & " sec" & vbCrLf & "Object property: " & (t3 - t2).TotalSeconds & " sec")
Console.ReadKey()
End Sub
End Module
Class InflationRow
Property IntProperty As Integer
Property StrProperty As String
End Class
Class InflationRowCollection
Property InflationList As New List(Of InflationRow)
ReadOnly Property InflationsObj(o As Object) As InflationRow 'use different name for testing, so we can compare
Get
If TypeOf o Is String Then
Return Inflations(DirectCast(o, String))
ElseIf TypeOf o Is Integer Then
Return Inflations(DirectCast(o, Integer))
Else
Throw New ArgumentException
End If
End Get
End Property
ReadOnly Property Inflations(k As String) As InflationRow
Get
For Each ir As InflationRow In InflationList
If ir.StrProperty = k Then Return ir
Next
Return Nothing
End Get
End Property
ReadOnly Property Inflations(k As Integer) As InflationRow
Get
For Each ir As InflationRow In InflationList
If ir.IntProperty = k Then Return ir
Next
Return Nothing
End Get
End Property
End Class

Get item in VBA Collection by Name

Ahoy hoy,
I'm trying to do stuff to a custom object in a custom collection by referencing it's name property in VBA Excel. I swear it worked before (or at least didn't throw an error) and now its kaput. I'm getting an invalid call or argument error when I try to Get something by a string. Thanks in advance for even reading this too, any help is appreciated. <\edit>
Here's the collection:
Option Explicit
Private DRAFields As New Collection
Sub Add(Name As String, Optional colNbr As Long, Optional Exists As Boolean)
Dim fld As New DRAFld
fld.colNbr = colNbr
fld.Name = Name
fld.Exists = Exists
DRAFields.Add fld
End Sub
Property Get Item(NameOrNumber As Variant)
Set Item = DRAFields(NameOrNumber) '<------- Error here
End Property
The collections has items added by passing an array of names in to a function and the collection is returned without issue. I can iterate over by using the key. But the error happens if get as such: Debug.Print myFlds.Item("Customer").colNbr
And the object class just in case:
Option Explicit
Private clmNbrPvt As Long
Private namePvt As String
Private existsPvt As Boolean
Public Property Get colNbr() As Long
colNbr = clmNbrPvt
End Property
Public Property Let colNbr(lngParam As Long)
clmNbrPvt = lngParam
End Property
Public Property Get Name() As String
Name = namePvt
End Property
Public Property Let Name(strParam As String)
namePvt = strParam
End Property
Public Property Get Exists() As Boolean
Exists = existsPvt
End Property
Public Property Let Exists(booParam As Boolean)
existsPvt = booParam
End Property
And why not that function too:
Function validateAndBuildDRAFields(ByRef arrReqFields() As String, _
inputSheet As Worksheet, _
Optional VBAModule As String) As clsDRAFields
Dim lEndCol As Long: lEndCol = Standard.zGetLastColumn(inputSheet, 1)
Dim i As Long
Dim x As Long
Dim intExit As Long
Dim myDRAFields As New clsDRAFields
Set validateAndBuildDRAFields = myDRAFields
'Builds myDRAFields items from arrReqFields
For i = LBound(arrReqFields) To UBound(arrReqFields)
myDRAFields.Add arrReqFields(i)
Next i
'checks if required fields exist on input sheet
'if found then sets column number and exists = true
For i = 1 To myDRAFields.Count
For x = 1 To lEndCol
If inputSheet.Cells(1, x) = myDRAFields.Item(i).Name Then
myDRAFields.Item(i).colNbr = x
myDRAFields.Item(i).Exists = True
intExit = intExit + 1
Exit For
End If
Next x
If intExit = UBound(arrReqFields) + 1 Then Exit For
Next i
' tells user if there are any missing fields and ends if true
If (Not intExit = UBound(arrReqFields) + 1) Or _
intExit = 0 Then
For i = 1 To myDRAFields.Count
If myDRAFields.Item(i).Exists = False Then
Call Standard.TheEndWithError("I couldn't find the " & myDRAFields.Item(i).Name & _
" column in your file. Please add " & myDRAFields.Item(i).Name & _
" to your DRA Layout.", False, VBAModule)
End If
Next i
Set myDRAFields = Nothing
Standard.TheEnd
End If
End Function
To access a collection item by its key, you have to supply a key when you add the item to the collection. The key is optional. When you access a collection item with a string, the Item method assumes you want to match the key. When you use an integer, it assumes you want the positional index.
So, change the line in your Add method to
DRAFields.Add fld, fld.Name
and you'll be able to access items by their Name property.

Accessing data from collection VBA

I'm trying to access data from collection by using .item(). What I am trying to do is to collect data in collection function fncPopCcyLst and access it by .item(1) in cbSortCcy to get the row number. This is a test to see if I can store several data in my collection and access them via .item(). However, I get a VBA runtime error '5'. Will someone guide me kindly what I am doing wrong? Thank you.
Below are my codes.
Class Module: clsSngGenUtl
Private prpSngStrVal As String
Private prpSngRowNum As Long
Private prpSngClmNum As Long
'++ Define properties
'== String row number
Public Property Get SngStrVal() As String
SngStrVal = prpSngStrVal
End Property
Public Property Let SngStrVal(ByRef varStrVal As String)
prpSngStrVal = varStrVal
End Property
'++ Define properties
'== Scalar row number
Public Property Get SngRowNum() As Long
SngRowNum = prpSngRowNum
End Property
Public Property Let SngRowNum(ByVal varRowNum As Long)
prpSngRowNum = varRowNum
End Property
'++ Define properties
'== Single column number
Public Property Get SngClmNum() As Long
SngClmNum = prpSngClmNum
End Property
Public Property Let SngClmNum(ByRef varClmNum As Long)
prpSngClmNum = varClmNum
End Property
'++ Define functions
'== function get row number
Public Function fncGetRowNum(ByRef varWbName As Workbook, ByVal varWsName As String, ByRef varSttClm As Long) As Long
On Error GoTo Exception
prpSngRowNum = 0
prpSngRowNum = varWbName.Sheets(Trim(varWsName)).Cells(Rows.Count, varSttClm).End(xlUp).Row
fncGetRowNum = prpSngRowNum
ExitHere:
Exit Function
Exception:
Resume ExitHere
End Function
'== function get column number
Public Function fncGetClmNum(ByRef varWbName As Workbook, ByVal varWsName As String, ByRef varSttRow As Long) As Long
On Error GoTo Exception
prpSngClmNum = 0
prpSngClmNum = varWbName.Sheets(Trim(varWsName)).Cells(varSttRow, Columns.Count).End(xlToLeft).Column
fncGetClmNum = prpSngClmNum
ExitHere:
Exit Function
Exception:
Resume ExitHere
End Function`
Below is my collection class: clsColCcySrt
'++ Declare variables
Private prpColCcySrt As Collection
'++ Define properties
Public Property Get ColCcySrt() As Collection
Set ColCcySrt = prpColCcySrt
End Property
Public Property Set ColCcySrt(varColCcy As Collection)
Set prpColCcySrt = varColCcy
End Property
Public Function fncGetCcyRow(ByRef varStrVal As String) As Long
On Error GoTo Exception
Dim clsSngGen As clsSngGenUtl
Dim varRowNum As Long
varRowNum = 0
For Each clsSngGen In Me.ColCcySrt
varRowNum = clsSngGen.SngRowNum()
Next clsSngGen
'== Return value
fncGetCcyRow = varRowNum
ExitHere:
Exit Function
Exception:
If fncGetCcyRow = 0 Then
MsgBox "Exception: Value is <" & fncGetCcyRow & ">."
End If
Resume ExitHere
End Function
`
"Regular" Module to populate array: fncPopFxLst
`
Public Function fncPopCcyLst(ByRef varWbName As String, ByRef varWsName As String, ByRef varCcyTyp As String) As Collection
Dim clnColCcy As Collection
Dim clsArrGen As clsArrGenUtl
Dim clsSngGen As clsSngGenUtl
Dim varWbName As Workbook
Set clnColCcy = New Collection
'== Start collecting items
Set clsSngGen = New clsSngGenUtl
Set varWbName = ThisWorkbook
clsSngGen.SngStrVal = "Reuters"
clsSngGen.SngRowNum = clsSngGen.fncGetRowNum(varWbName, varWsName, 1)
clnColCcy.Add clsSngGen
Set fncPopCcyLst = clnColCcy
End Function
`
Lastly, the subroutine `
Private Sub cbSortCcy()
Dim clsColCcy As clsColCcySrt
Dim varDirPth As String
Dim varCcySrc As String
Dim varWsStrg As String
Dim varWbStrg As String
varDirPth = tbDirectoryName & "\" & tbFileName
varCcySrc = "Currency"
varWsStrg = "List"
varWbStrg = varDirPth
Set clsColCcy = New clsColCcySrt
Set clsColCcy.ColCcySrt = fncPopCcyLst(varWbStrg, varWsStrg, varCcySrc)
'Debug.Princ clsColCcy.fncGetCcyRow("Reuters")
Debug.Print clsColCcy.ColCcySrt.Item(1)
End Sub
`
VBA Run-time error '5' is:
"Invalid procedure call or argument"
In your class clsColCcySrt, you have a line:
varRowNum = clsSngGen.SngRowNum()
which would be correct if SngRowNum were a function and not a property. Remove the parentheses () to call the property.

Extend Collections Class VBA

I have created a sort function to allow a collection of instances of a custom object to be sorted based on one of the objects properties. Is it possible to extend the existing collections class in VBA? I do not believe inheritance is supported in VBA, so I am not sure how to go about this in the proper way. I could just create a new module and place the function in that module, but that doesn't seem like the best way of doing it.
Thanks for the responses. I ended up creating my own class which extends the Collections class in VBA. Below is the code if anyone is interested.
'Custom collections class is based on the Collections class, this class extendes that
'functionallity so that the sort method for a collection of objects is part of
'the class.
'One note on this class is that in order to make this work in VBA, the Attribute method has to be added
'manually. To do this, create the class, then export it out of the project. Open in a text editor and
'add this line Attribute Item.VB_UserMemId = 0 under the Item() function and this line
'Attribute NewEnum.VB_UserMemId = -4 under the NewEnum() function. Save and import back into project.
'This allows the Procedure Attribute to be recognized.
Option Explicit
Private pCollection As Collection
Private Sub Class_Initialize()
Set pCollection = New Collection
End Sub
Private Sub Class_Terminate()
Set pCollection = Nothing
End Sub
Function NewEnum() As IUnknown
Set NewEnum = pCollection.[_NewEnum]
End Function
Public Function Count() As Long
Count = pCollection.Count
End Function
Public Function item(key As Variant) As clsCustomCollection
item = pCollection(key)
End Function
'Implements a selection sort algorithm, could likely be improved, but meets the current need.
Public Sub SortByProperty(sortPropertyName As String, sortAscending As Boolean)
Dim item As Object
Dim i As Long
Dim j As Long
Dim minIndex As Long
Dim minValue As Variant
Dim testValue As Variant
Dim swapValues As Boolean
Dim sKey As String
For i = 1 To pCollection.Count - 1
Set item = pCollection(i)
minValue = CallByName(item, sortPropertyName, VbGet)
minIndex = i
For j = i + 1 To pCollection.Count
Set item = pCollection(j)
testValue = CallByName(item, sortPropertyName, VbGet)
If (sortAscending) Then
swapValues = (testValue < minValue)
Else
swapValues = (testValue > minValue)
End If
If (swapValues) Then
minValue = testValue
minIndex = j
End If
Set item = Nothing
Next j
If (minIndex <> i) Then
Set item = pCollection(minIndex)
pCollection.Remove minIndex
pCollection.Add item, , i
Set item = Nothing
End If
Set item = Nothing
Next i
End Sub
Public Sub Add(value As Variant, key As Variant)
pCollection.Add value, key
End Sub
Public Sub Remove(key As Variant)
pCollection.Remove key
End Sub
Public Sub Clear()
Set m_PrivateCollection = New Collection
End Sub
One popular option is to use an ADO disconnected recordset as a sort of hyperpowered collection/dictionary object, which has built-in support for Sort. Although you are using ADO, you don't need a database.
I would create a wrapper class that exposes the collection object's properties, substituting the sort function with your own.