Attribute VB_Name = "Collections"
Option Compare Database
Option Explicit
' Note that STRING INDEXED ARRAYS are called "Dictionary". Available from Windows Scripting Runtime.
' SORTING ARRAYS OF User Defined Types: http://www.dailydoseofexcel.com/archives/2006/02/23/sorting-arrays-of-user-defined-types/
' For HeapSort: http://www.source-code.biz/snippets/vbasic/6.htm
'***********************************************************************************************
'THE MERGESORT ALGORITHM FOR SORTING IN O(n.log(n)) TIME - Applied to VBA COLLECTION objects...
'***********************************************************************************************
' © 2005-2011 Matthew Slyman. Copying, modification and distribution in software is permitted.
' Attribution of work to author is required, and unauthorised redistribution is not permitted.
' Copyright notice must remain intact.
Public Function MergeSortCollection(ByRef CollectionToSort As Collection, Optional ByVal OrderByProperty As String, Optional ByVal OrderByType As String, Optional ByVal InDescendingOrder As Boolean = False, Optional DISTINCT As Boolean = False) As Collection ' Optional CompareMode As VbCompareMethod = vbTextCompare ' - potentially useful for Strings ''' Optional identify_by_obj_guid As Boolean = True ' - alternative being to identify by Parameters. See below under "=Potentially fixable weaknesses of this routine:==="
' >>> What about ORDERing by Array or Collection of properties?
On Error GoTo Failed
If CollectionToSort.Count > 1 Then
If LenB(OrderByType) = 0 Then ' If sorting by a Variant, the OrderByType parameter enables the programmer to specify how to sort it (numerical or string based sorting). Otherwise, the VBA code below can automatically detect the data type of the sorting/comparison variable.
Dim testVar As Variant ' <<< Should perhaps be using the IsObject function... Investigate whether this would result in a more reliable SortByMerge function. Think about the potential use of default Value.
If LenB(OrderByProperty) = 0 Then
testVar = CollectionToSort(1)
Else
testVar = CollectionToSort(1).Properties(OrderByProperty)
End If
OrderByType = TypeName(testVar)
End If
' >>> Need to think about USER-DEFINED TYPES! And how to use Properties() in them! Remember that user-defined types are NOT Objects... << Actually, Collection objects themselves do not appear to handle UDTs (user-defined types) gracefully at all - so it is very unlikely that someone would be using this routine on a Collection of UDT-variables at all.
Select Case OrderByType ' VarType function results: vbNull; vbInteger; vbLong; vbSingle; vbDouble; vbCurrency; vbDate; vbString; vbObject; vbError; vbBoolean; vbVariant; vbDataObject; vbDecimal; vbByte; vbUserDefinedType; vbArray
Case "Single", "Double", "String", "Integer", "Long", "Byte", "Currency", "Decimal", "Date": ' Boolean?
Case Else:
Err.Raise number:=vbObjectError + 1, Source:="AAA.Collections.MergeSortCollection", Description:="OrderBy Type not recognized. Use Single, Double, String, Integer, Long, Byte, Currency, Decimal or Date"
End Select
' <<< Might push the stuff above this line into a separate initialization function, for efficiency reasons.
End If
Dim SortedCollection As New Collection
Select Case CollectionToSort.Count
Case 0, 1:
Set MergeSortCollection = CollectionToSort
Case Else:
Dim Size1 As Long, Size2 As Long, CollectionToSortSize As Long, counter As Long
Dim Collection1 As New Collection, Collection2 As New Collection
CollectionToSortSize = CollectionToSort.Count
Size1 = Round(CollectionToSortSize/2, 0)
Size2 = CollectionToSortSize - Size1
For counter = 1 To CollectionToSort.Count
If counter <= Size1 Then Collection1.Add CollectionToSort(counter) Else Collection2.Add CollectionToSort(counter)
Next counter
Set MergeSortCollection = MergeInOrder(MergeSortCollection(Collection1, OrderByProperty, OrderByType, InDescendingOrder, DISTINCT), MergeSortCollection(Collection2, OrderByProperty, OrderByType, InDescendingOrder, DISTINCT), OrderByProperty, OrderByType, InDescendingOrder, DISTINCT)
End Select
Exit Function
Failed:
Debug.Print "#ERROR# " & Err.number & " : " & Err.Source & ".Collections.MergeSortCollection " & vbCrLf & " - " & Err.Description
Err.Clear
End Function
Private Function MergeInOrder(ByRef Collection1 As Collection, ByRef Collection2 As Collection, Optional OrderByProperty As String = "", Optional OrderByType As String = "String", Optional ByVal InDescendingOrder As Boolean = False, Optional DISTINCT As Boolean = False) As Collection
' The other half of the MERGESORT algorithm, for COLLECTIONS... An auxiliary function for the recursive MergeSort function... The first function splits the Collections successively into halves, and then this function merges the halves in order, successively, until the resulting sorted Collection is returned.
' >> NEED to use . dot delimited Properties for multiple levels of objects... Could also replace with Collection. Automatically determine the types of those properties. Sort accordingly.
' >>> Yet to rigorously test sorting stability (to see whether function preserves original ordering as far as possible). Appears to do so... Just want to make sure...
' >>> Yet to rigorously test worst-case space complexity. Appears to be O(n) but just want to make sure it is in practice...
Dim SortedCollection As New Collection
Dim Counter1 As Long, Counter2 As Long
Counter1 = 1
Counter2 = 1
Dim ComparisonFlag As Boolean
Do While Counter1 <= Collection1.Count And Counter2 <= Collection2.Count
Dim ComparisonVariable1 As Variant, ComparisonVariable2 As Variant
If DISTINCT Then
Dim IdenticalNodes As Boolean ' Not necessary to compare e.g. Collection1(1) with Collection1(2) because Collection1 itself will already have been split and merged, and recursively tested for identical elements via this MergeInOrder function.
If Not (LenB(OrderByProperty) <> 0) Then ' <<< Should perhaps be using the IsObject function... Investigate whether this would result in a more reliable SortByMerge function.
IdenticalNodes = (Collection1(Counter1) = Collection2(Counter2))
Else
IdenticalNodes = (Collection1(Counter1) Is Collection2(Counter2))
End If
If IdenticalNodes Then
SortedCollection.Add Collection1(Counter1)
Counter1 = Counter1 + 1 ' Already inserted into SortedCollection.
Counter2 = Counter2 + 1 ' Pass over the duplicate.
GoTo SkipComparison
End If
End If
If Not (LenB(OrderByProperty) <> 0) Then
ComparisonVariable1 = Collection1(Counter1)
ComparisonVariable2 = Collection2(Counter2)
Else
ComparisonVariable1 = Collection1(Counter1).Properties(OrderByProperty)
ComparisonVariable2 = Collection2(Counter2).Properties(OrderByProperty)
End If
Select Case OrderByType ' Using a text-based parameter, rather than automatically detecting type,
Case "Boolean": ComparisonFlag = CBool(ComparisonVariable1) < CBool(ComparisonVariable2) ' << WARNING: Numeric representation of "True" constant depends on system implementation. e.g. VBA (INT -1) differs from SQL Server (BIT 1) in this respect. Is TRUE<FALSE or is FALSE>TRUE?
Case "Single": ComparisonFlag = CSng(ComparisonVariable1) < CSng(ComparisonVariable2)
Case "Double": ComparisonFlag = CDbl(ComparisonVariable1) < CDbl(ComparisonVariable2)
Case "String": ComparisonFlag = (-1 = Strings.StrComp(CStr(ComparisonVariable1), CStr(ComparisonVariable2), vbTextCompare))
Case "Integer", "Long", "Byte": ComparisonFlag = CLng(ComparisonVariable1) < CLng(ComparisonVariable2)
Case "Currency": ComparisonFlag = CCur(ComparisonVariable1) < CCur(ComparisonVariable2) ' What about comparison of dissimilar currencies in heterogeneous forex environment?
Case "Decimal": ComparisonFlag = CDec(ComparisonVariable1) < CDec(ComparisonVariable2)
Case "Date": ComparisonFlag = CDate(ComparisonVariable1) < CDate(ComparisonVariable2)
End Select
If InDescendingOrder Then ComparisonFlag = Not ComparisonFlag
If ComparisonFlag Then
SortedCollection.Add Collection1(Counter1)
Counter1 = Counter1 + 1
Else
SortedCollection.Add Collection2(Counter2)
Counter2 = Counter2 + 1
End If
SkipComparison:
Loop
Do While Counter1 <= Collection1.Count
SortedCollection.Add Collection1(Counter1)
Counter1 = Counter1 + 1
Loop
Do While Counter2 <= Collection2.Count
SortedCollection.Add Collection2(Counter2)
Counter2 = Counter2 + 1
Loop
Set Collection1 = Nothing
Set Collection2 = Nothing
Set MergeInOrder = SortedCollection
' Set SortedCollection = Nothing ' Would this not muck up the results of the function? Remember, MergeInOrder is still set by Object Ref to SortedCollection. They are essentially the same object...
End Function
' END OF MERGESORT FOR COLLECTIONS
IIRC .item (ARG), .Remove (Arg) будет принимать либо индекс или ключ в качестве арг. Похоже, вы тестируете коллекцию целых чисел. VB, возможно, не сможет сказать, имеете ли вы значение индекса или ключа, поэтому попробуйте проверить набор строк. – xidgel
Это возвращает частично заполненную коллекцию, которую я вижу, работая в журналах, спасибо! Как я могу сделать эту работу для коллекции Integer? –