-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWeb MiscFunctions.txt
2549 lines (2287 loc) · 119 KB
/
Web MiscFunctions.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Attribute VB_Name = "MiscFunctions"
Private Const BIF_RETURNONLYFSDIRS As Long = &H1
Private Const BIF_DONTGOBELOWDOMAIN As Long = &H2
Private Const BIF_RETURNFSANCESTORS As Long = &H8
Private Const BIF_BROWSEFORCOMPUTER As Long = &H1000
Private Const BIF_BROWSEFORPRINTER As Long = &H2000
Private Const BIF_BROWSEINCLUDEFILES As Long = &H4000
Private Const MAX_PATH As Long = 260
Type BrowseInfo
hOwner As Long
pidlRoot As Long
pszDisplayName As String
lpszINSTRUCTIONS As String
ulFlags As Long
lpfn As Long
lParam As Long
iImage As Long
End Type
Type SHFILEOPSTRUCT
hwnd As Long
wFunc As Long
pFrom As String
pTo As String
fFlags As Integer
fAnyOperationsAborted As Boolean
hNameMappings As Long
lpszProgressTitle As String
End Type
Declare Function SHGetPathFromIDListA Lib "shell32.dll" ( _
ByVal pidl As Long, _
ByVal pszBuffer As String) As Long
Declare Function SHBrowseForFolderA Lib "shell32.dll" ( _
lpBrowseInfo As BrowseInfo) As Long
Public Function IsEven(ByVal Number As Long) As Boolean
IsEven = (Number Mod 2 = 0)
End Function
Function BrowseFolder(Optional Caption As String = "") As String
Dim BrowseInfo As BrowseInfo
Dim FolderName As String
Dim ID As Long
Dim Res As Long
With BrowseInfo
.hOwner = 0
.pidlRoot = 0
.pszDisplayName = String$(MAX_PATH, vbNullChar)
.lpszINSTRUCTIONS = Caption
.ulFlags = BIF_RETURNONLYFSDIRS
.lpfn = 0
End With
FolderName = String$(MAX_PATH, vbNullChar)
ID = SHBrowseForFolderA(BrowseInfo)
If ID Then
Res = SHGetPathFromIDListA(ID, FolderName)
If Res Then
BrowseFolder = Left$(FolderName, InStr(FolderName, vbNullChar) - 1)
End If
End If
End Function
Public Function FormChecks()
' See if the combo box values have been selected
If frmSetUp.cboxProject.Value = "" Then
MsgBox "Please select a valid project.", vbExclamation
FormChecks = "Fail"
Exit Function
End If
If frmSetUp.cboxTestPhase.Value = "" Then
MsgBox "Please select a valid test phase.", vbExclamation
FormChecks = "Fail"
Exit Function
End If
End Function
Public Function FindWeekends(ByVal dateStart As Date, ByVal dateEnd As Date)
Dim myArr()
Dim iCount As Integer
iCount = 0
' Set the array to at least an empty array
ReDim Preserve myArr(iCount)
myArr(iCount) = ""
' Loop round all dates in range
For myDate = dateStart To dateEnd
Select Case Weekday(myDate)
Case vbSaturday
ReDim Preserve myArr(iCount)
myArr(iCount) = myDate + 2
iCount = iCount + 1
End Select
Next
' See if we've got an array or not
If myArr(0) = "" Then
' We've only got weeks or days to use so return this
myArr(0) = "No Weeks"
End If
FindWeekends = myArr
End Function
Public Function FindMonths(ByVal dateStart As Date, ByVal dateEnd As Date)
Dim myArr()
Dim iCount As Integer
Dim blnBroke As Boolean
iCount = 0
' Set the array to at least an empty array
ReDim Preserve myArr(iCount)
myArr(iCount) = ""
' Loop round removing months at a time
Do
rc = DateAdd("m", 1, dateStart)
If rc > dateEnd Then
blnBroke = True
Else
ReDim Preserve myArr(iCount)
myArr(iCount) = rc
dateStart = rc
iCount = iCount + 1
End If
Loop Until blnBroke = True
' Find the number of days from the current start date to the end date
rc = DateDiff("d", dateStart, dateEnd)
' See if we've got an array or not
If myArr(0) = "" Then
' We've only got weeks or days to use so return this
myArr(0) = "No Month"
Else
' See if we're over half way through a month or not
If rc > 15 Then
rc = DateAdd("m", 1, dateStart)
If rc <= dateEnd Then
ReDim Preserve myArr(iCount)
myArr(iCount) = rc
End If
Else
' Add these days to the last end date in the array
myArr(UBound(myArr)) = DateAdd("d", rc, myArr(UBound(myArr)))
End If
' See if any of our months are weekends
For i = 0 To UBound(myArr)
If Weekday(myArr(i)) = 1 Then
myArr(i) = DateAdd("d", 1, myArr(i))
Else
If Weekday(myArr(i)) = 7 Then
myArr(i) = DateAdd("d", 2, myArr(i))
End If
End If
Next
End If
FindMonths = myArr
End Function
Public Function FindPreviousCol(ByVal strPrevStatus As String)
Select Case strPrevStatus
Case "New"
iPrevCol = 2
Case "Assigned"
iPrevCol = 3
Case "Open", "In Progress"
iPrevCol = 4
Case "Fixed"
iPrevCol = 5
Case "Ready For Testing", "Ready for Testing"
iPrevCol = 6
Case "Failed Testing"
iPrevCol = 7
Case "Tested"
iPrevCol = 8
Case "Reopen"
iPrevCol = 9
Case "Duplicate"
iPrevCol = 10
Case "Rejected"
iPrevCol = 11
Case "On Hold"
iPrevCol = 12
Case "Closed", "Status_Closed"
iPrevCol = 13
End Select
FindPreviousCol = iPrevCol
End Function
Public Function AccumulateData()
' Set up the status array
Dim arrStatus(12)
arrStatus(0) = "New"
arrStatus(1) = "Assigned"
arrStatus(2) = "Open"
arrStatus(3) = "Fixed"
arrStatus(4) = "Tested"
arrStatus(5) = "Ready For Testing"
arrStatus(6) = "Failed Testing"
arrStatus(7) = "Reopen"
arrStatus(8) = "Duplicate"
arrStatus(9) = "Rejected"
arrStatus(10) = "On Hold"
arrStatus(11) = "Closed"
Dim iPrev
Dim iCurr
Dim iTot
' Get the dates in the date dictionary into an array
myDates = objDateDictionary.Keys
' Loop through the date array
For i = 0 To UBound(myDates)
' If we're on iteration 0 then don't add
If i <> 0 Then
' Loop through the status
For j = 0 To UBound(arrStatus)
' Get the previous value
iPrev = objDatesStatusDictionary.Item(myDates(i - 1) & "|" & arrStatus(j))
' Get the current value for this date
iCurr = objDatesStatusDictionary.Item(myDates(i) & "|" & arrStatus(j))
' Add the two together
iTot = iPrev + iCurr
' Write this new value to the current date
objDatesStatusDictionary.Item(myDates(i) & "|" & arrStatus(j)) = iTot
Next
End If
Next
End Function
Public Function RemoveProjectList()
' Make the project data worksheet displayed
ThisWorkbook.Worksheets("Project Data").Visible = True
' Clear the project data sheet
ThisWorkbook.Worksheets("Project Data").Activate
ThisWorkbook.Worksheets("Project Data").Select
iTotalRows = ThisWorkbook.Worksheets("Project Data").UsedRange.Rows.Count
If iTotalRows <> 1 Then
For iRow = 2 To iTotalRows
ThisWorkbook.Worksheets("Project Data").Rows(iRow & ":" & iRow).Select
Selection.Delete Shift:=xlUp
If iRow = 2 Then
If ThisWorkbook.Worksheets("Project Data").Cells(iRow, 1).Value = "" Then
Exit For
Else
iRow = iRow - 1
End If
End If
Next
End If
' Make the project data worksheet hidden
ThisWorkbook.Worksheets("Project Data").Visible = False
End Function
Public Function GetWeekday(ByVal dateTheDate As Date)
iDay = Weekday(dateTheDate)
Select Case iDay
Case 1
GetWeekday = "Sunday"
Case 2
GetWeekday = "Monday"
Case 3
GetWeekday = "Tuesday"
Case 4
GetWeekday = "Wednesday"
Case 5
GetWeekday = "Thursday"
Case 6
GetWeekday = "Friday"
Case 7
GetWeekday = "Saturday"
End Select
End Function
Public Function FindLastCol(ByVal strWorksheet As String, ByVal iRow As Integer)
Dim LastCol As Long
With objWrkBk.Worksheets(strWorksheet)
FindLastCol = .Cells(iRow, .Columns.Count).End(xlToLeft).Column
End With
End Function
Public Function FindLastRow(ByVal strWorksheet As String, ByVal iCol As Integer)
Dim LastCol As Long
With objWrkBk.Worksheets(strWorksheet)
FindLastRow = .Cells(.Rows.Count, iCol).End(xlUp).Row
End With
End Function
Public Sub SortDictionary(Dict As Scripting.Dictionary, _
SortByKey As Boolean, _
Optional Descending As Boolean = False, _
Optional CompareMode As VbCompareMethod = vbTextCompare)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' SortDictionary
' This sorts a Dictionary object. If SortByKey is False, the
' the sort is done based on the Items of the Dictionary, and
' these items must be simple data types. They may not be
' Object, Arrays, or User-Defined Types. If SortByKey is True,
' the Dictionary is sorted by Key value, and the Items in the
' Dictionary may be Object as well as simple variables.
'
' If sort by key is True, all element of the Dictionary
' must have a non-blank Key value. If Key is vbNullString
' the procedure will terminate.
'
' By defualt, sorting is done in Ascending order. You can
' sort by Descending order by setting the Descending parameter
' to True.
'
' By default, text comparisons are done case-INSENSITIVE (e.g.,
' "a" = "A"). To use case-SENSITIVE comparisons (e.g., "a" <> "A")
' set CompareMode to vbBinaryCompare.
'
' Note: This procedure requires the
' QSortInPlace function, which is described and available for
' download at www.cpearson.com/excel/qsort.htm .
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Ndx As Long
Dim KeyValue As String
Dim ItemValue As Variant
Dim Arr() As Variant
Dim KeyArr() As String
Dim VTypes() As VbVarType
Dim V As Variant
Dim SplitArr As Variant
Dim TempDict As Scripting.Dictionary
'''''''''''''''''''''''''''''
' Ensure Dict is not Nothing.
'''''''''''''''''''''''''''''
If Dict Is Nothing Then
Exit Sub
End If
''''''''''''''''''''''''''''
' If the number of elements
' in Dict is 0 or 1, no
' sorting is required.
''''''''''''''''''''''''''''
If (Dict.Count = 0) Or (Dict.Count = 1) Then
Exit Sub
End If
''''''''''''''''''''''''''''
' Create a new TempDict.
''''''''''''''''''''''''''''
Set TempDict = New Scripting.Dictionary
If SortByKey = True Then
''''''''''''''''''''''''''''''''''''''''
' We're sorting by key. Redim the Arr
' to the number of elements in the
' Dict object, and load that array
' with the key names.
''''''''''''''''''''''''''''''''''''''''
ReDim Arr(0 To Dict.Count - 1)
For Ndx = 0 To Dict.Count - 1
Arr(Ndx) = Dict.Keys(Ndx)
Next Ndx
''''''''''''''''''''''''''''''''''''''
' Sort the key names.
''''''''''''''''''''''''''''''''''''''
QSortInPlace InputArray:=Arr, LB:=-1, UB:=-1, Descending:=Descending, CompareMode:=CompareMode
''''''''''''''''''''''''''''''''''''''''''''
' Load TempDict. The key value come from
' our sorted array of keys Arr, and the
' Item comes from the original Dict object.
''''''''''''''''''''''''''''''''''''''''''''
For Ndx = 0 To Dict.Count - 1
KeyValue = Arr(Ndx)
TempDict.Add Key:=KeyValue, Item:=Dict.Item(KeyValue)
Next Ndx
'''''''''''''''''''''''''''''''''
' Set the passed in Dict object
' to our TempDict object.
'''''''''''''''''''''''''''''''''
Set Dict = TempDict
''''''''''''''''''''''''''''''''
' This is the end of processing.
''''''''''''''''''''''''''''''''
Else
'''''''''''''''''''''''''''''''''''''''''''''''
' Here, we're sorting by items. The Items must
' be simple data types. They may NOT be Objects,
' arrays, or UserDefineTypes.
' First, ReDim Arr and VTypes to the number
' of elements in the Dict object. Arr will
' hold a string containing
' Item & vbNullChar & Key
' This keeps the association between the
' item and its key.
'''''''''''''''''''''''''''''''''''''''''''''''
ReDim Arr(0 To Dict.Count - 1)
ReDim VTypes(0 To Dict.Count - 1)
For Ndx = 0 To Dict.Count - 1
If (IsObject(Dict.Items(Ndx)) = True) Or _
(IsArray(Dict.Items(Ndx)) = True) Or _
VarType(Dict.Items(Ndx)) = vbUserDefinedType Then
Debug.Print "***** ITEM IN DICTIONARY WAS OBJECT OR ARRAY OR UDT"
Exit Sub
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Here, we create a string containing
' Item & vbNullChar & Key
' This preserves the associate between an item and its
' key. Store the VarType of the Item in the VTypes
' array. We'll use these values later to convert
' back to the proper data type for Item.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Arr(Ndx) = Dict.Items(Ndx) & vbNullChar & Dict.Keys(Ndx)
VTypes(Ndx) = VarType(Dict.Items(Ndx))
Next Ndx
''''''''''''''''''''''''''''''''''
' Sort the array that contains the
' items of the Dictionary along
' with their associated keys
''''''''''''''''''''''''''''''''''
QSortInPlace InputArray:=Arr, LB:=-1, UB:=-1, Descending:=Descending, CompareMode:=vbTextCompare
For Ndx = LBound(Arr) To UBound(Arr)
'''''''''''''''''''''''''''''''''''''
' Loop trhogh the array of sorted
' Items, Split based on vbNullChar
' to get the Key from the element
' of the array Arr.
SplitArr = Split(Arr(Ndx), vbNullChar)
''''''''''''''''''''''''''''''''''''''''''
' It may have been possible that item in
' the dictionary contains a vbNullChar.
' Therefore, use UBound to get the
' key value, which will necessarily
' be the last item of SplitArr.
' Then Redim Preserve SplitArr
' to UBound - 1 to get rid of the
' Key element, and use Join
' to reassemble to original value
' of the Item.
'''''''''''''''''''''''''''''''''''''''''
KeyValue = SplitArr(UBound(SplitArr))
ReDim Preserve SplitArr(LBound(SplitArr) To UBound(SplitArr) - 1)
ItemValue = Join(SplitArr, vbNullChar)
'''''''''''''''''''''''''''''''''''''''
' Join will set ItemValue to a string
' regardless of what the original
' data type was. Test the VTypes(Ndx)
' value to convert ItemValue back to
' the proper data type.
'''''''''''''''''''''''''''''''''''''''
Select Case VTypes(Ndx)
Case vbBoolean
ItemValue = CBool(ItemValue)
Case vbByte
ItemValue = CByte(ItemValue)
Case vbCurrency
ItemValue = CCur(ItemValue)
Case vbDate
ItemValue = CDate(ItemValue)
Case vbDecimal
ItemValue = CDec(ItemValue)
Case vbDouble
ItemValue = CDbl(ItemValue)
Case vbInteger
ItemValue = CInt(ItemValue)
Case vbLong
ItemValue = CLng(ItemValue)
Case vbSingle
ItemValue = CLng(ItemValue)
Case vbString
ItemValue = CStr(ItemValue)
Case Else
ItemValue = ItemValue
End Select
''''''''''''''''''''''''''''''''''''''
' Finally, add the Item and Key to
' our TempDict dictionary.
TempDict.Add Key:=KeyValue, Item:=ItemValue
Next Ndx
End If
'''''''''''''''''''''''''''''''''
' Set the passed in Dict object
' to our TempDict object.
'''''''''''''''''''''''''''''''''
Set Dict = TempDict
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' modQSortInPlace
' By Chip Pearson, www.cpearson.com, [email protected]
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' This module contains the QSortInPlace procedure and private supporting procedures.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Function QSortInPlace( _
ByRef InputArray As Variant, _
Optional ByVal LB As Long = -1&, _
Optional ByVal UB As Long = -1&, _
Optional ByVal Descending As Boolean = False, _
Optional ByVal CompareMode As VbCompareMethod = vbTextCompare, _
Optional ByVal NoAlerts As Boolean = False) As Boolean
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' QSortInPlace
'
' This function sorts the array InputArray in place -- this is, the original array in the
' calling procedure is sorted. It will work with either string data or numeric data.
' It need not sort the entire array. You can sort only part of the array by setting the LB and
' UB parameters to the first (LB) and last (UB) element indexes that you want to sort.
' LB and UB are optional parameters. If omitted LB is set to the LBound of InputArray, and if
' omitted UB is set to the UBound of the InputArray. If you want to sort the entire array,
' omit the LB and UB parameters, or set both to -1, or set LB = LBound(InputArray) and set
' UB to UBound(InputArray).
'
' By default, the sort method is case INSENSTIVE (case doens't matter: "A", "b", "C", "d").
' To make it case SENSITIVE (case matters: "A" "C" "b" "d"), set the CompareMode argument
' to vbBinaryCompare (=0). If Compare mode is omitted or is any value other than vbBinaryCompare,
' it is assumed to be vbTextCompare and the sorting is done case INSENSITIVE.
'
' The function returns TRUE if the array was successfully sorted or FALSE if an error
' occurred. If an error occurs (e.g., LB > UB), a message box indicating the error is
' displayed. To suppress message boxes, set the NoAlerts parameter to TRUE.
'
''''''''''''''''''''''''''''''''''''''
' MODIFYING THIS CODE:
''''''''''''''''''''''''''''''''''''''
' If you modify this code and you call "Exit Procedure", you MUST decrment the RecursionLevel
' variable. E.g.,
' If SomethingThatCausesAnExit Then
' RecursionLevel = RecursionLevel - 1
' Exit Function
' End If
'''''''''''''''''''''''''''''''''''''''
'
' Note: If you coerce InputArray to a ByVal argument, QSortInPlace will not be
' able to reference the InputArray in the calling procedure and the array will
' not be sorted.
'
' This function uses the following procedures. These are declared as Private procedures
' at the end of this module:
' IsArrayAllocated
' IsSimpleDataType
' IsSimpleNumericType
' QSortCompare
' NumberOfArrayDimensions
' ReverseArrayInPlace
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Temp As Variant
Dim Buffer As Variant
Dim CurLow As Long
Dim CurHigh As Long
Dim CurMidpoint As Long
Dim Ndx As Long
Dim pCompareMode As VbCompareMethod
'''''''''''''''''''''''''
' 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
Select Case NumberOfArrayDimensions(InputArray)
Case 0
''''''''''''''''''''''''''''''''''''''''''
' Zero dimensions indicates an unallocated
' dynamic array.
''''''''''''''''''''''''''''''''''''''''''
If NoAlerts = False Then
MsgBox "The InputArray is an empty, unallocated array."
End If
RecursionLevel = RecursionLevel - 1
Exit Function
Case 1
''''''''''''''''''''''''''''''''''''''''''
' We sort ONLY single dimensional arrays.
''''''''''''''''''''''''''''''''''''''''''
Case Else
''''''''''''''''''''''''''''''''''''''''''
' We sort ONLY single dimensional arrays.
''''''''''''''''''''''''''''''''''''''''''
If NoAlerts = False Then
MsgBox "The InputArray is multi-dimensional." & _
"QSortInPlace works only on single-dimensional arrays."
End If
RecursionLevel = RecursionLevel - 1
Exit Function
End Select
'''''''''''''''''''''''''''''''''''''''''''''''''''
' Ensure that InputArray is an array of simple data
' types, not other arrays or objects. This tests
' the data type of only the first element of
' InputArray. If InputArray is an array of Variants,
' subsequent data types may not be simple data types
' (e.g., they may be objects or other arrays), and
' this may cause QSortInPlace to fail on the StrComp
' operation.
'''''''''''''''''''''''''''''''''''''''''''''''''''
If IsSimpleDataType(InputArray(LBound(InputArray))) = False Then
If NoAlerts = False Then
MsgBox "InputArray is not an array of simple data types."
RecursionLevel = RecursionLevel - 1
Exit Function
End If
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''
' ensure that the LB parameter is valid.
''''''''''''''''''''''''''''''''''''''''''''''''''''
Select Case LB
Case Is < LBound(InputArray)
If NoAlerts = False Then
MsgBox "The LB lower bound parameter is less than the LBound of the InputArray"
End If
RecursionLevel = RecursionLevel - 1
Exit Function
Case Is > UBound(InputArray)
If NoAlerts = False Then
MsgBox "The LB lower bound parameter is greater than the UBound of the InputArray"
End If
RecursionLevel = RecursionLevel - 1
Exit Function
Case Is > UB
If NoAlerts = False Then
MsgBox "The LB lower bound parameter is greater than the UB upper bound parameter."
End If
RecursionLevel = RecursionLevel - 1
Exit Function
End Select
''''''''''''''''''''''''''''''''''''''''''''''''''''
' 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
End If ' RecursionLevel = 1
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' 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
CurMidpoint = (LB + UB) \ 2 ' note integer division (\) here
Temp = InputArray(CurMidpoint)
Do While (CurLow <= CurHigh)
Do While QSortCompare(V1:=InputArray(CurLow), V2:=Temp, CompareMode:=pCompareMode) < 0
CurLow = CurLow + 1
If CurLow = UB Then
Exit Do
End If
Loop
Do While QSortCompare(V1:=Temp, V2:=InputArray(CurHigh), CompareMode:=pCompareMode) < 0
CurHigh = CurHigh - 1
If CurHigh = LB Then
Exit Do
End If
Loop
If (CurLow <= CurHigh) Then
Buffer = InputArray(CurLow)
InputArray(CurLow) = InputArray(CurHigh)
InputArray(CurHigh) = Buffer
CurLow = CurLow + 1
CurHigh = CurHigh - 1
End If
Loop
If LB < CurHigh Then
QSortInPlace InputArray:=InputArray, LB:=LB, UB:=CurHigh, _
Descending:=Descending, CompareMode:=pCompareMode, NoAlerts:=True
End If
If CurLow < UB Then
QSortInPlace InputArray:=InputArray, LB:=CurLow, UB:=UB, _
Descending:=Descending, CompareMode:=pCompareMode, NoAlerts:=True
End If
'''''''''''''''''''''''''''''''''''''
' 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
ReverseArrayInPlace InputArray
End If
End If
RecursionLevel = RecursionLevel - 1
QSortInPlace = True
End Function
Private Function QSortCompare(V1 As Variant, V2 As Variant, _
Optional CompareMode As VbCompareMethod = vbTextCompare) As Long
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' QSortCompare
' This function is used in QSortInPlace to compare two elements. If
' V1 AND V2 are both numeric data types (integer, long, single, double)
' they are converted to Doubles and compared. If V1 and V2 are BOTH strings
' that contain numeric data, they are converted to Doubles and compared.
' If either V1 or V2 is a string and does NOT contain numeric data, both
' V1 and V2 are converted to Strings and compared with StrComp.
'
' The result is -1 if V1 < V2,
' 0 if V1 = V2
' 1 if V1 > V2
' For text comparisons, case sensitivity is controlled by CompareMode.
' If this is vbBinaryCompare, the result is case SENSITIVE. If this
' is omitted or any other value, the result is case INSENSITIVE.
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim D1 As Double
Dim D2 As Double
Dim S1 As String
Dim S2 As String
Dim Compare As VbCompareMethod
''''''''''''''''''''''''''''''''''''''''''''''''
' Test CompareMode. Any value other than
' vbBinaryCompare will default to vbTextCompare.
''''''''''''''''''''''''''''''''''''''''''''''''
If CompareMode = vbBinaryCompare Or CompareMode = vbTextCompare Then
Compare = CompareMode
Else
Compare = vbTextCompare
End If
'''''''''''''''''''''''''''''''''''''''''''''''
' If either V1 or V2 is either an array or
' an Object, raise a error 13 - Type Mismatch.
'''''''''''''''''''''''''''''''''''''''''''''''
If IsArray(V1) = True Or IsArray(V2) = True Then
Err.Raise 13
Exit Function
End If
If IsObject(V1) = True Or IsObject(V2) = True Then
Err.Raise 13
Exit Function
End If
If IsSimpleNumericType(V1) = True Then
If IsSimpleNumericType(V2) = True Then
'''''''''''''''''''''''''''''''''''''
' If BOTH V1 and V2 are numeric data
' types, then convert to Doubles and
' do an arithmetic compare and
' return the result.
'''''''''''''''''''''''''''''''''''''
D1 = CDbl(V1)
D2 = CDbl(V2)
If D1 = D2 Then
QSortCompare = 0
Exit Function
End If
If D1 < D2 Then
QSortCompare = -1
Exit Function
End If
If D1 > D2 Then
QSortCompare = 1
Exit Function
End If
End If
End If
''''''''''''''''''''''''''''''''''''''''''''
' Either V1 or V2 was not numeric data type.
' Test whether BOTH V1 AND V2 are numeric
' strings. If BOTH are numeric, convert to
' Doubles and do a arithmetic comparison.
''''''''''''''''''''''''''''''''''''''''''''
If IsNumeric(V1) = True And IsNumeric(V2) = True Then
D1 = CDbl(V1)
D2 = CDbl(V2)
If D1 = D2 Then
QSortCompare = 0
Exit Function
End If
If D1 < D2 Then
QSortCompare = -1
Exit Function
End If
If D1 > D2 Then
QSortCompare = 1
Exit Function
End If
End If
''''''''''''''''''''''''''''''''''''''''''''''
' Either or both V1 and V2 was not numeric
' string. In this case, convert to Strings
' and use StrComp to compare.
''''''''''''''''''''''''''''''''''''''''''''''
S1 = CStr(V1)
S2 = CStr(V2)
QSortCompare = StrComp(S1, S2, Compare)
End Function
Private Function NumberOfArrayDimensions(Arr As Variant) As Integer
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' NumberOfArrayDimensions
' This function returns the number of dimensions of an array. An unallocated dynamic array
' has 0 dimensions. This condition can also be tested with IsArrayEmpty.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Ndx As Integer
Dim Res As Integer
On Error Resume Next
' Loop, increasing the dimension index Ndx, until an error occurs.
' An error will occur when Ndx exceeds the number of dimension
' in the array. Return Ndx - 1.
Do
Ndx = Ndx + 1
Res = UBound(Arr, Ndx)
Loop Until Err.Number <> 0
NumberOfArrayDimensions = Ndx - 1
End Function
Private Function ReverseArrayInPlace(InputArray As Variant, _
Optional NoAlerts As Boolean = False) As Boolean
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' ReverseArrayInPlace
' This procedure reverses the order of an array in place -- this is, the array variable
' in the calling procedure is sorted. An error will occur if InputArray is not an array,
'if it is an empty, unallocated array, or if the number of dimensions is not 1.
'
' NOTE: Before calling the ReverseArrayInPlace procedure, consider if your needs can
' be met by simply reading the existing array in reverse order (Step -1). If so, you can save
' the overhead added to your application by calling this function.
'
' The function returns TRUE if the array was successfully reversed, or FALSE if
' an error occurred.
'
' If an error occurred, a message box is displayed indicating the error. To suppress
' the message box and simply return FALSE, set the NoAlerts parameter to TRUE.
'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim Temp As Variant
Dim Ndx As Long
Dim Ndx2 As Long
''''''''''''''''''''''''''''''''
' 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