VBA数组sortingfunction?

我正在为VBA中的数组寻找一个体面的sorting实现。 快速sorting将是首选。 或者任何其他sortingalgorithm,除了泡沫或合并就足够了。

请注意,这是与MS Project 2003一起工作的,所以应避免使用任何与Excel相关的本地函数。

看看这里 :

Public Sub QuickSort(vArray As Variant, inLow As Long, inHi As Long) Dim pivot As Variant Dim tmpSwap As Variant Dim tmpLow As Long Dim tmpHi As Long tmpLow = inLow tmpHi = inHi pivot = vArray((inLow + inHi) \ 2) While (tmpLow <= tmpHi) While (vArray(tmpLow) < pivot And tmpLow < inHi) tmpLow = tmpLow + 1 Wend While (pivot < vArray(tmpHi) And tmpHi > inLow) tmpHi = tmpHi - 1 Wend If (tmpLow <= tmpHi) Then tmpSwap = vArray(tmpLow) vArray(tmpLow) = vArray(tmpHi) vArray(tmpHi) = tmpSwap tmpLow = tmpLow + 1 tmpHi = tmpHi - 1 End If Wend If (inLow < tmpHi) Then QuickSort vArray, inLow, tmpHi If (tmpLow < inHi) Then QuickSort vArray, tmpLow, inHi End Sub 

如果有其他人需要,我将“快速sorting”algorithm转换为VBA。

我已经优化了运行在一个Int / Longs数组,但它应该很简单,将其转换为一个工作在任意可比较的元素。

 Private Sub QuickSort(ByRef a() As Long, ByVal l As Long, ByVal r As Long) Dim M As Long, i As Long, j As Long, v As Long M = 4 If ((r - l) > M) Then i = (r + l) / 2 If (a(l) > a(i)) Then swap a, l, i '// Tri-Median Methode!' If (a(l) > a(r)) Then swap a, l, r If (a(i) > a(r)) Then swap a, i, r j = r - 1 swap a, i, j i = l v = a(j) Do Do: i = i + 1: Loop While (a(i) < v) Do: j = j - 1: Loop While (a(j) > v) If (j < i) Then Exit Do swap a, i, j Loop swap a, i, r - 1 QuickSort a, l, j QuickSort a, i + 1, r End If End Sub Private Sub swap(ByRef a() As Long, ByVal i As Long, ByVal j As Long) Dim T As Long T = a(i) a(i) = a(j) a(j) = T End Sub Private Sub InsertionSort(ByRef a(), ByVal lo0 As Long, ByVal hi0 As Long) Dim i As Long, j As Long, v As Long For i = lo0 + 1 To hi0 v = a(i) j = i Do While j > lo0 If Not a(j - 1) > v Then Exit Do a(j) = a(j - 1) j = j - 1 Loop a(j) = v Next i End Sub Public Sub sort(ByRef a() As Long) QuickSort a, LBound(a), UBound(a) InsertionSort a, LBound(a), UBound(a) End Sub 

在德国的解释 ,但代码是一个经过充分testing的就地实施:

 Private Sub QuickSort(ByRef Field() As String, ByVal LB As Long, ByVal UB As Long) Dim P1 As Long, P2 As Long, Ref As String, TEMP As String P1 = LB P2 = UB Ref = Field((P1 + P2) / 2) Do Do While (Field(P1) < Ref) P1 = P1 + 1 Loop Do While (Field(P2) > Ref) P2 = P2 - 1 Loop If P1 <= P2 Then TEMP = Field(P1) Field(P1) = Field(P2) Field(P2) = TEMP P1 = P1 + 1 P2 = P2 - 1 End If Loop Until (P1 > P2) If LB < P2 Then Call QuickSort(Field, LB, P2) If P1 < UB Then Call QuickSort(Field, P1, UB) End Sub 

像这样调用:

 Call QuickSort(MyArray, LBound(MyArray), UBound(MyArray)) 

我发布了一些代码来回答StackOverflow上的相关问题:

sortingVBA中的multidimensional array

该线程中的代码示例包括:

  1. vector数组Quicksort;
  2. 多列数组QuickSort;
  3. 一个BubbleSort。

Alain的优化Quicksort非常有光泽:我只是做了一个基本的拆分和recursion,但上面的代码示例有一个“门控”function,可以减less重复值的冗余比较。 另一方面,我代码为Excel,而防御性编码的方式有点多 – 要警告,如果数组包含有害的“Empty()”变体,则会需要它,这将破坏您的While。 。find比较运算符,并将你的代码陷入无限循环。

请注意,quicksort algorthms(以及任何recursionalgorithm)可以填充堆栈并使Excel崩溃。 如果你的数组less于1024个成员,我会使用一个基本的BubbleSort。

 Public Sub QuickSortArray(ByRef SortArray As Variant,_
                                可选lngMin As Long = -1,_ 
                                可选lngMax As Long = -1,_ 
                                可选lngColumn As Long = 0)
在错误恢复下一步 
'sorting一个二维数组
'样例用法:按第3列的内容对arrData进行sorting “ 'QuickSortArray arrData,,,3
“ “由Jim Rech 10/20/98发表Excel.Programming
“修改,Nigel Heffernan:
''逃脱与空变体的比较失败 '防守编码:检查input
昏暗我只要 Dim j As Long Dim varMid As Variant Dim arrRowTemp As Variant 昏暗的lngColTemp只要

如果IsEmpty(SortArray)那么 退出小组 万一
如果InStr(TypeName(SortArray),“()”)<1那么IsArray()有些破碎:在types名称中查找括号 退出小组 万一
如果lngMin = -1那么 lngMin = LBound(SortArray,1) 万一
如果lngMax = -1那么 lngMax = UBound(SortArray,1) 万一
如果lngMin> = lngMax那么'不需要sorting 退出小组 万一

我= lngMin j = lngMax
varMid =空 varMid = SortArray((lngMin + lngMax)\ 2,lngColumn)
'我们发送'空'和无效的数据项目到列表的末尾: 如果IsObject(varMid)然后'请注意,我们不检查isObject(SortArray(n)) - varMid 可能会拿起一个有效的默认成员或属性 我= lngMax j = lngMin ElseIf IsEmpty(varMid)然后 我= lngMax j = lngMin ElseIf IsNull(varMid)然后 我= lngMax j = lngMin ElseIf varMid =“”然后 我= lngMax j = lngMin ElseIf varType(varMid)= vbError Then 我= lngMax j = lngMin ElseIf varType(varMid)> 17然后 我= lngMax j = lngMin 万一

而我<= j
而SortArray(我,lngColumn)<varMid和我<lngMax 我=我+ 1 蜿蜒
而varMid <SortArray(j,lngColumn)和j> lngMin j = j-1 蜿蜒

如果我<= j那么
“交换行 ReDim arrRowTemp(LBound(SortArray,2)到UBound(SortArray,2)) 对于lngColTemp = LBound(SortArray,2)到UBound(SortArray,2) arrRowTemp(lngColTemp)= SortArray(i,lngColTemp) SortArray(i,lngColTemp)= SortArray(j,lngColTemp) SortArray(j,lngColTemp)= arrRowTemp(lngColTemp) 下一个lngColTemp 擦除arrRowTemp
我=我+ 1 j = j-1
万一

蜿蜒
如果(lngMin <j)那么调用QuickSortArray(SortArray,lngMin,j,lngColumn) 如果(i <lngMax)则调用QuickSortArray(SortArray,i,lngMax,lngColumn)

结束小组

自然数(string)快速sorting

只是为了堆积在话题上。 通常情况下,如果你用数字sortingstring,你会得到这样的东西:

  Text1 Text10 Text100 Text11 Text2 Text20 

但是你真的希望它能识别数值,并按照sorting

  Text1 Text2 Text10 Text11 Text20 Text100 

以下是如何做到这一点…

注意:

  • 我很久以前从网上偷了快速sorting,不知道现在在哪里…
  • 我翻译了原来用C语言编写的CompareNaturalNum函数。
  • 与其他Q-Sort的区别:如果BottomTemp = TopTemp,则不交换值

自然数快速sorting

 Public Sub QuickSortNaturalNum(strArray() As String, intBottom As Integer, intTop As Integer) Dim strPivot As String, strTemp As String Dim intBottomTemp As Integer, intTopTemp As Integer intBottomTemp = intBottom intTopTemp = intTop strPivot = strArray((intBottom + intTop) \ 2) Do While (intBottomTemp <= intTopTemp) ' < comparison of the values is a descending sort Do While (CompareNaturalNum(strArray(intBottomTemp), strPivot) < 0 And intBottomTemp < intTop) intBottomTemp = intBottomTemp + 1 Loop Do While (CompareNaturalNum(strPivot, strArray(intTopTemp)) < 0 And intTopTemp > intBottom) ' intTopTemp = intTopTemp - 1 Loop If intBottomTemp < intTopTemp Then strTemp = strArray(intBottomTemp) strArray(intBottomTemp) = strArray(intTopTemp) strArray(intTopTemp) = strTemp End If If intBottomTemp <= intTopTemp Then intBottomTemp = intBottomTemp + 1 intTopTemp = intTopTemp - 1 End If Loop 'the function calls itself until everything is in good order If (intBottom < intTopTemp) Then QuickSortNaturalNum strArray, intBottom, intTopTemp If (intBottomTemp < intTop) Then QuickSortNaturalNum strArray, intBottomTemp, intTop End Sub 

自然数比较(用于快速sorting)

 Function CompareNaturalNum(string1 As Variant, string2 As Variant) As Integer 'string1 is less than string2 -1 'string1 is equal to string2 0 'string1 is greater than string2 1 Dim n1 As Long, n2 As Long Dim iPosOrig1 As Integer, iPosOrig2 As Integer Dim iPos1 As Integer, iPos2 As Integer Dim nOffset1 As Integer, nOffset2 As Integer If Not (IsNull(string1) Or IsNull(string2)) Then iPos1 = 1 iPos2 = 1 Do While iPos1 <= Len(string1) If iPos2 > Len(string2) Then CompareNaturalNum = 1 Exit Function End If If isDigit(string1, iPos1) Then If Not isDigit(string2, iPos2) Then CompareNaturalNum = -1 Exit Function End If iPosOrig1 = iPos1 iPosOrig2 = iPos2 Do While isDigit(string1, iPos1) iPos1 = iPos1 + 1 Loop Do While isDigit(string2, iPos2) iPos2 = iPos2 + 1 Loop nOffset1 = (iPos1 - iPosOrig1) nOffset2 = (iPos2 - iPosOrig2) n1 = Val(Mid(string1, iPosOrig1, nOffset1)) n2 = Val(Mid(string2, iPosOrig2, nOffset2)) If (n1 < n2) Then CompareNaturalNum = -1 Exit Function ElseIf (n1 > n2) Then CompareNaturalNum = 1 Exit Function End If ' front padded zeros (put 01 before 1) If (n1 = n2) Then If (nOffset1 > nOffset2) Then CompareNaturalNum = -1 Exit Function ElseIf (nOffset1 < nOffset2) Then CompareNaturalNum = 1 Exit Function End If End If ElseIf isDigit(string2, iPos2) Then CompareNaturalNum = 1 Exit Function Else If (Mid(string1, iPos1, 1) < Mid(string2, iPos2, 1)) Then CompareNaturalNum = -1 Exit Function ElseIf (Mid(string1, iPos1, 1) > Mid(string2, iPos2, 1)) Then CompareNaturalNum = 1 Exit Function End If iPos1 = iPos1 + 1 iPos2 = iPos2 + 1 End If Loop Else If IsNull(string1) And Not IsNull(string2) Then CompareNaturalNum = -1 Exit Function ElseIf IsNull(string1) And IsNull(string2) Then CompareNaturalNum = 0 Exit Function ElseIf Not IsNull(string1) And IsNull(string2) Then CompareNaturalNum = 1 Exit Function End If End If End Function 

isDigit(用于CompareNaturalNum)

 Function isDigit(ByVal str As String, pos As Integer) As Boolean Dim iCode As Integer If pos <= Len(str) Then iCode = Asc(Mid(str, pos, 1)) If iCode >= 48 And iCode <= 57 Then isDigit = True End If End Function 

你不想要一个基于Excel的解决scheme,但是因为我今天遇到了同样的问题,并且想要使用其他Office应用程序函数进行testing,所以我编写了下面的函数。

限制:

  • 二维数组;
  • 最多3列作为sorting键;
  • 取决于Excel;

经过testing,从Visio 2010调用Excel 2010


 Option Base 1 Private Function sort_array_2D_excel(array_2D, array_sortkeys, Optional array_sortorders, Optional tag_header As String = "Guess", Optional tag_matchcase As String = "False") ' Dependencies: Excel; Tools > References > Microsoft Excel [Version] Object Library Dim excel_application As Excel.Application Dim excel_workbook As Excel.Workbook Dim excel_worksheet As Excel.Worksheet Set excel_application = CreateObject("Excel.Application") excel_application.Visible = True excel_application.ScreenUpdating = False excel_application.WindowState = xlNormal Set excel_workbook = excel_application.Workbooks.Add excel_workbook.Activate Set excel_worksheet = excel_workbook.Worksheets.Add excel_worksheet.Activate excel_worksheet.Visible = xlSheetVisible Dim excel_range As Excel.Range Set excel_range = excel_worksheet.Range("A1").Resize(UBound(array_2D, 1) - LBound(array_2D, 1) + 1, UBound(array_2D, 2) - LBound(array_2D, 2) + 1) excel_range = array_2D For i_sortkey = LBound(array_sortkeys) To UBound(array_sortkeys) If IsNumeric(array_sortkeys(i_sortkey)) Then sortkey_range = Chr(array_sortkeys(i_sortkey) + 65 - 1) & "1" Set array_sortkeys(i_sortkey) = excel_worksheet.Range(sortkey_range) Else MsgBox "Error in sortkey parameter:" & vbLf & "array_sortkeys(" & i_sortkey & ") = " & array_sortkeys(i_sortkey) & vbLf & "Terminating..." End End If Next i_sortkey For i_sortorder = LBound(array_sortorders) To UBound(array_sortorders) Select Case LCase(array_sortorders(i_sortorder)) Case "asc" array_sortorders(i_sortorder) = XlSortOrder.xlAscending Case "desc" array_sortorders(i_sortorder) = XlSortOrder.xlDescending Case Else array_sortorders(i_sortorder) = XlSortOrder.xlAscending End Select Next i_sortorder Select Case LCase(tag_header) Case "yes" tag_header = Excel.xlYes Case "no" tag_header = Excel.xlNo Case "guess" tag_header = Excel.xlGuess Case Else tag_header = Excel.xlGuess End Select Select Case LCase(tag_matchcase) Case "true" tag_matchcase = True Case "false" tag_matchcase = False Case Else tag_matchcase = False End Select Select Case (UBound(array_sortkeys) - LBound(array_sortkeys) + 1) Case 1 Call excel_range.Sort(Key1:=array_sortkeys(1), Order1:=array_sortorders(1), Header:=tag_header, MatchCase:=tag_matchcase) Case 2 Call excel_range.Sort(Key1:=array_sortkeys(1), Order1:=array_sortorders(1), Key2:=array_sortkeys(2), Order2:=array_sortorders(2), Header:=tag_header, MatchCase:=tag_matchcase) Case 3 Call excel_range.Sort(Key1:=array_sortkeys(1), Order1:=array_sortorders(1), Key2:=array_sortkeys(2), Order2:=array_sortorders(2), Key3:=array_sortkeys(3), Order3:=array_sortorders(3), Header:=tag_header, MatchCase:=tag_matchcase) Case Else MsgBox "Error in sortkey parameter:" & vbLf & "Maximum number of sort columns is 3!" & vbLf & "Currently passed: " & (UBound(array_sortkeys) - LBound(array_sortkeys) + 1) End End Select For i_row = 1 To excel_range.Rows.Count For i_column = 1 To excel_range.Columns.Count array_2D(i_row, i_column) = excel_range(i_row, i_column) Next i_column Next i_row excel_workbook.Close False excel_application.Quit Set excel_worksheet = Nothing Set excel_workbook = Nothing Set excel_application = Nothing sort_array_2D_excel = array_2D End Function 

这是如何testing函数的一个例子:

 Private Sub test_sort() array_unsorted = dim_sort_array() Call msgbox_array(array_unsorted) array_sorted = sort_array_2D_excel(array_unsorted, Array(2, 1, 3), Array("desc", "", "asdas"), "yes", "False") Call msgbox_array(array_sorted) End Sub Private Function dim_sort_array() Dim array_unsorted(1 To 5, 1 To 3) As String i_row = 0 i_row = i_row + 1 array_unsorted(i_row, 1) = "Column1": array_unsorted(i_row, 2) = "Column2": array_unsorted(i_row, 3) = "Column3" i_row = i_row + 1 array_unsorted(i_row, 1) = "OR": array_unsorted(i_row, 2) = "A": array_unsorted(i_row, 3) = array_unsorted(i_row, 1) & "_" & array_unsorted(i_row, 2) i_row = i_row + 1 array_unsorted(i_row, 1) = "XOR": array_unsorted(i_row, 2) = "A": array_unsorted(i_row, 3) = array_unsorted(i_row, 1) & "_" & array_unsorted(i_row, 2) i_row = i_row + 1 array_unsorted(i_row, 1) = "NOT": array_unsorted(i_row, 2) = "B": array_unsorted(i_row, 3) = array_unsorted(i_row, 1) & "_" & array_unsorted(i_row, 2) i_row = i_row + 1 array_unsorted(i_row, 1) = "AND": array_unsorted(i_row, 2) = "A": array_unsorted(i_row, 3) = array_unsorted(i_row, 1) & "_" & array_unsorted(i_row, 2) dim_sort_array = array_unsorted End Function Sub msgbox_array(array_2D, Optional string_info As String = "2D array content:") msgbox_string = string_info & vbLf For i_row = LBound(array_2D, 1) To UBound(array_2D, 1) msgbox_string = msgbox_string & vbLf & i_row & vbTab For i_column = LBound(array_2D, 2) To UBound(array_2D, 2) msgbox_string = msgbox_string & array_2D(i_row, i_column) & vbTab Next i_column Next i_row MsgBox msgbox_string End Sub 

如果有人使用其他版本的办公室进行testing,请在这里发布,如果有任何问题。

谢谢!

L.

我认为我的代码(testing)更“受过教育”,假设越简单越好

 Option Base 1 'Function to sort an array decscending Function SORT(Rango As Range) As Variant Dim check As Boolean check = True If IsNull(Rango) Then check = False End If If check Then Application.Volatile Dim x() As Variant, n As Double, m As Double, i As Double, j As Double, k As Double n = Rango.Rows.Count: m = Rango.Columns.Count: k = n * m ReDim x(n, m) For i = 1 To n Step 1 For j = 1 To m Step 1 x(i, j) = Application.Large(Rango, k) k = k - 1 Next j Next i SORT = x Else Exit Function End If End Function 

我不知道你会说这个数组sorting代码。 实现起来很快,而且这个工作还没有testing过大的数组。 它适用于一维数组,对于多维的附加值,重定位matrix需要被构build(与初始数组相比less一个维)。

  For AR1 = LBound(eArray, 1) To UBound(eArray, 1) eValue = eArray(AR1) For AR2 = LBound(eArray, 1) To UBound(eArray, 1) If eArray(AR2) < eValue Then eArray(AR1) = eArray(AR2) eArray(AR2) = eValue eValue = eArray(AR1) End If Next AR2 Next AR1 

这是我用来在内存中sorting – 它可以很容易地扩展到sorting数组。

  Sub sortlist() Dim xarr As Variant Dim yarr As Variant Dim zarr As Variant xarr = Sheets("sheet").Range("sing col range") ReDim yarr(1 To UBound(xarr), 1 To 1) ReDim zarr(1 To UBound(xarr), 1 To 1) For n = 1 To UBound(xarr) zarr(n, 1) = 1 Next n For n = 1 To UBound(xarr) - 1 y = zarr(n, 1) For a = n + 1 To UBound(xarr) If xarr(n, 1) > xarr(a, 1) Then y = y + 1 Else zarr(a, 1) = zarr(a, 1) + 1 End If Next a yarr(y, 1) = xarr(n, 1) Next n y = zarr(UBound(xarr), 1) yarr(y, 1) = xarr(UBound(xarr), 1) yrng = "A1:A" & UBound(yarr) Sheets("sheet").Range(yrng) = yarr End Sub 
 Dim arr As Object Dim InputArray 'Creating a array list Set arr = CreateObject("System.Collections.ArrayList") 'String InputArray = Array("d", "c", "b", "a", "f", "e", "g") 'number 'InputArray = Array(6, 5, 3, 4, 2, 1) ' adding the elements in the array to array_list For Each element In InputArray arr.Add element Next 'sorting happens arr.Sort 'Converting ArrayList to an array 'so now a sorted array of elements is stored in the array sorted_array. sorted_array = arr.toarray