VBA array sort function?

后端 未结 13 1946
北荒
北荒 2020-11-22 05:28

I\'m looking for a decent sort implementation for arrays in VBA. A Quicksort would be preferred. Or any other sort algorithm other than bubble or merge would suffice.

<
相关标签:
13条回答
  • 2020-11-22 06:02

    This is what I use to sort in memory - it can easily be expanded to sort an array.

    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
    
    0 讨论(0)
  • 2020-11-22 06:03

    Explanation in German but the code is a well-tested in-place implementation:

    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
    

    Invoked like this:

    Call QuickSort(MyArray, LBound(MyArray), UBound(MyArray))
    
    0 讨论(0)
  • 2020-11-22 06:07

    I converted the 'fast quick sort' algorithm to VBA, if anyone else wants it.

    I have it optimized to run on an array of Int/Longs but it should be simple to convert it to one that works on arbitrary comparable elements.

    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
    
    0 讨论(0)
  • 2020-11-22 06:11
    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
    
    0 讨论(0)
  • 2020-11-22 06:12

    @Prasand Kumar, here's a complete sort routine based on Prasand's concepts:

    Public Sub ArrayListSort(ByRef SortArray As Variant)
        '
        'Uses the sort capabilities of a System.Collections.ArrayList object to sort an array of values of any simple
        'data-type.
        '
        'AUTHOR: Peter Straton
        '
        'CREDIT: Derived from Prasand Kumar's post at: https://stackoverflow.com/questions/152319/vba-array-sort-function
        '
        '*************************************************************************************************************
    
        Static ArrayListObj As Object
        Dim i As Long
        Dim LBnd As Long
        Dim UBnd As Long
    
        LBnd = LBound(SortArray)
        UBnd = UBound(SortArray)
    
        'If necessary, create the ArrayList object, to be used to sort the specified array's values
    
        If ArrayListObj Is Nothing Then
            Set ArrayListObj = CreateObject("System.Collections.ArrayList")
        Else
            ArrayListObj.Clear  'Already allocated so just clear any old contents
        End If
    
        'Add the ArrayList elements from the array of values to be sorted. (There appears to be no way to do this
        'using a single assignment statement.)
    
        For i = LBnd To UBnd
            ArrayListObj.Add SortArray(i)
        Next i
    
        ArrayListObj.Sort   'Do the sort
    
        'Transfer the sorted ArrayList values back to the original array, which can be done with a single assignment
        'statement.  But the result is always zero-based so then, if necessary, adjust the resulting array to match
        'its original index base.
    
        SortArray = ArrayListObj.ToArray
        If LBnd <> 0 Then ReDim Preserve SortArray(LBnd To UBnd)
    End Sub
    
    0 讨论(0)
  • 2020-11-22 06:15

    Natural Number (Strings) Quick Sort

    Just to pile onto the topic. Normally, if you sort strings with numbers you'll get something like this:

        Text1
        Text10
        Text100
        Text11
        Text2
        Text20
    

    But you really want it to recognize the numerical values and be sorted like

        Text1
        Text2
        Text10
        Text11
        Text20
        Text100
    

    Here's how to do it...

    Note:

    • I stole the Quick Sort from the internet a long time ago, not sure where now...
    • I translated the CompareNaturalNum function which was originally written in C from the internet as well.
    • Difference from other Q-Sorts: I don't swap the values if the BottomTemp = TopTemp

    Natural Number Quick Sort

    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
    

    Natural Number Compare(Used in Quick Sort)

    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
            ' Everything was the same so far, check if Len(string2) > Len(String1)
            ' If so, then string1 < string2
            If Len(string2) > Len(string1) Then
                CompareNaturalNum = -1
                Exit Function
            End If
        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(Used in 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
    
    0 讨论(0)
提交回复
热议问题