Excel vba массивы сортировка

Сортировка одномерного массива в VBA Excel по возрастанию или убыванию с числовым или текстовым сравнением числовых элементов. Сортировка выбором.

Сортировка массива выбором

Функция, осуществляющая сортировку выбором одномерного массива по возрастанию с числовым сравнением числовых элементов массива:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

Function SortArray(myArray As Variant) As Variant

Dim l As Long, u As Long, i1 As Long, i2 As Long, im As Long, tmp As Variant

‘Определение наименьшего индекса массива

l = LBound(myArray)

‘Определение наибольшего индекса массива

u = UBound(myArray)

    For i1 = l To u

        im = i1

        For i2 = i1 To u

            ‘Поиск наименьшего элемента массива, начиная с элемента myArray(i1)

            If myArray(i2) < myArray(im) Then im = i2

        Next

        ‘Если наименьший элемент не является текущим (im <> i1),

        ‘тогда наименьший элемент и текущий меняются местами

        If im <> i1 Then

            tmp = myArray(i1)

            myArray(i1) = myArray(im)

            myArray(im) = tmp

        End If

    Next

SortArray = myArray

End Function

Внешний цикл сравнивает по очереди каждый элемент массива с наименьшем элементом, найденным вложенным циклом среди оставшихся элементов (с бóльшими индексами), и, если наименьший элемент myArray(im) не является текущим элементом myArray(i1), они меняются местами.

Функция с выбором вида сортировки

Функция сортировки одномерного массива с дополнительным параметром mySort, который позволяет выбрать сортировку по возрастанию или убыванию и с числовым или текстовым сравнением числовых элементов массива:

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

Function SortArrayNew(myArray As Variant, mySort As Long) As Variant

Dim l As Long, u As Long, i1 As Long, i2 As Long, im As Long, tmp As Variant

l = LBound(myArray)

u = UBound(myArray)

    For i1 = l To u

        im = i1

            For i2 = i1 To u

                Select Case mySort

                    Case Is = 0

                        If myArray(i2) < myArray(im) Then im = i2

                    Case Is = 1

                        If CStr(myArray(i2)) < CStr(myArray(im)) Then im = i2

                    Case Is = 2

                        If myArray(i2) > myArray(im) Then im = i2

                    Case Is = 3

                        If CStr(myArray(i2)) > CStr(myArray(im)) Then im = i2

                End Select

            Next

        If im <> i1 Then

            tmp = myArray(i1)

            myArray(i1) = myArray(im)

            myArray(im) = tmp

        End If

    Next

SortArrayNew = myArray

End Function

Значения, которые может принимать параметр mySort:

Значение Вид сортировки
0 по возрастанию с числовым сравнением чисел
1 по возрастанию с текстовым сравнением чисел
2 по убыванию с числовым сравнением чисел
3 по убыванию с текстовым сравнением чисел

Пример сортировки массива

Пример использования разных видов сортировки одномерного массива в VBA Excel с выводом результатов на рабочий лист:

Sub Primer()

Dim myArr As Variant, x As Variant, i As Long, n As Long

    For i = 1 To 4

        myArr = Array(46, 25, «Лето», 508, 35, «лес», 11, 5, «25ф», «Лес», 45, 58, «лето», 350)

                myArr = SortArrayNew(myArr, i 1)

        n = 1

            For Each x In myArr

                Cells(n, i) = myArr(n 1)

                n = n + 1

            Next

    Next

End Sub

Результаты разных видов сортировки массива myArr:

Home / VBA / Arrays / VBA Sort Array

To sort an array in VBA, you need to write a code where you can match the first element of the array with the next one and inter-change them if the first one needs to come before. You need a FOR LOOP (For Next) for this and the UCASE function.

In this tutorial, we will see both ways (A-Z and Z-A) to sort elements of an array.

Sort an Array (A-Z)

In the below code, you have an array with five elements, and these elements have values starting from E to A, and now we need to sort in A-Z order.

Steps to Sort an Array

  1. First, you need to write a nested loop with For Next using the upper and lower bounds of the array.
  2. After that, within that loop, you need to use the VBA IF Statement to compare the two elements.
  3. From here, if the first element needs to come before the next element you need to interchange them saving their values in variables.
  4. In the end, use the “Next” keyword to close the loop.
Option Base 1
Sub vba_sort_array_a_to_z()

Dim myArray() As Variant
ReDim myArray(5)
Dim i As Integer
Dim j As Integer
Dim Temp As String

myArray(1) = "E"
myArray(2) = "D"
myArray(3) = "C"
myArray(4) = "B"
myArray(5) = "A"

'sorting array from A to Z
For i = LBound(myArray) To UBound(myArray)
    For j = i + 1 To UBound(myArray)
        If UCase(myArray(i)) > UCase(myArray(j)) Then
            Temp = myArray(j)
            myArray(j) = myArray(i)
            myArray(i) = Temp
        End If
    Next j
Next i

Debug.Print myArray(1)
Debug.Print myArray(2)
Debug.Print myArray(3)
Debug.Print myArray(4)
Debug.Print myArray(5)

End Sub

Make sure to activate the Immediate Window to see the result there.

Sort an Array (Z-A)

In the same way, you can sort from Z-A. In the earlier method, we compared the first element with the next element in the sequence, but in this code, you need to do the opposite. So if the second (next in sequence) element is greater than the first one the code will interchange them.

Option Base 1
Sub vba_sort_array_z_to_a()

Dim myArray() As Variant
ReDim myArray(5)
Dim i As Integer
Dim j As Integer
Dim Temp As String

myArray(1) = "A"
myArray(2) = "B"
myArray(3) = "C"
myArray(4) = "D"
myArray(5) = "E"

'sorting array from A to Z
For i = LBound(myArray) To UBound(myArray)
    For j = i + 1 To UBound(myArray)
        If UCase(myArray(i)) < UCase(myArray(j)) Then
            Temp = myArray(j)
            myArray(j) = myArray(i)
            myArray(i) = Temp
        End If
    Next j
Next i

Debug.Print myArray(1)
Debug.Print myArray(2)
Debug.Print myArray(3)
Debug.Print myArray(4)
Debug.Print myArray(5)

End Sub

Return to VBA Code Examples

This tutorial will demonstrate how to Sort values in an Array in VBA

Sorting a One-Dimensional Array using a loop

Sorting an Array requires a bit of data manipulation using loops, variables and temporary arrays.

  • You first need to populate the array with your values
  • You then need to loop through the array twice!  Once to get a value from the current element array, and the while still in that loop, to get the value of the next element in the array.
  • You then need to compare the elements – and move the 2nd one to the position of the first one if the 2nd one is alphabetically BEFORE the 1st one.

The example below demonstrates this procedure.

Sub SortAnArray()
   Dim i As Long
'Set the array
   Dim strName() As Variant
   Dim Temp As Variant
'populate the array
   strName() = Array("Bob Smith", "John Davies", "Fred Jones", "Steve Jenkins", "Bob Williams")
'loop through bound of the arry and get the first name
   For i = LBound(strName) To UBound(strName) - 1
'loop through again, and check if the next name is alphabetically before or after the original
      For j = i + 1 To UBound(strName)
         If UCase(strName(i)) > UCase(strName(j)) Then
'if the name needs to be moved before the previous name, add to a temp array
            Temp = strName(j)
'swop the names
            strName(j) = strName(i)
            strName(i) = Temp
         End If
     Next j
   Next i
'Output the Array through a message box
   MsgBox Join(strName(), vbCrLf)
End Sub

If you run this procedure, you would get the following message box.

vba sort array forwards

You can also sort the array in the other direction – eg: Z to A by changing this line of code

If UCase(strName(i)) > UCase(strName(j)) Then

to this line of code

If UCase(strName(i)) < UCase(strName(j)) Then

You would then get the following message box.

vba sort array backwards

VBA Coding Made Easy

Stop searching for VBA code online. Learn more about AutoMacro — A VBA Code Builder that allows beginners to code procedures from scratch with minimal coding knowledge and with many time-saving features for all users!
vba save as

Learn More!

ThreeWave
Sorting Array In VBA

This page describes procedures for sorting arrays of values in VBA.
ShortFadeBar

SectionBreak

The VBA language has no support for sorting the values stored in an array. One method that can be used to sort
arrays is to put the data on to a worksheet, sort the data on the worksheet, and then read back the values from
the worksheet into the array. The other method for sorting arrays is to use the QSort algorithm to sort the array
in place. This page describes both methods, with variations on the QSort method.

This section describes code that uses Excel’s range sorting method to sort the values in an array. The code first
creates a new worksheet so that this code won’t clash with existing data on a worksheet. Then, it loads the values
in the array to a range on the new worksheet, begining in cell A1. That range is sorted
and the data is read back into the array in VBA. The code for SortViaWorksheet is shown below.

Sub SortViaWorksheet()
    Dim Arr(1 To 5) As String 
    Dim WS As Worksheet 
    Dim R As Range
    Dim N As Long
    
    
    Arr(1) = "aaa"
    Arr(2) = "zzz"
    Arr(3) = "mmm"
    Arr(4) = "ttt"
    Arr(5) = "bbb"
    
    Application.ScreenUpdating = False
    
    
    Set WS = ThisWorkbook.Worksheets.Add
    
    
    Set R = WS.Range("A1").Resize(UBound(Arr) - LBound(Arr) + 1, 1)
    R = Application.Transpose(Arr)
    
    
    R.Sort key1:=R, order1:=xlAscending, MatchCase:=False
    
    
    For N = 1 To R.Rows.Count
        Arr(N) = R(N, 1)
    Next N
    
    
    Application.DisplayAlerts = False
    WS.Delete
    Application.DisplayAlerts = True
    Application.ScreenUpdating = True
    
    
    For N = LBound(Arr) To UBound(Arr)
        Debug.Print Arr(N)
    Next N   
End Sub

The SortViaWorksheet function works if you are using Excel and the structure of the workbook is
not protected. It the workbook is protected, you’ll get an error when creating the new sheet, so you will have to have a
scratch sheet in place beforehand or use an unused region of an existing (and unprotected) worksheet. Due to these limitations,
coupled with the fact that VBA is used in many applications other than Excel, it may be desirable to employ a
VBA-only method that doesn’t rely on any outside objects.

Sorting by any method is an expensive operations, especially with large arrays, due to the number of swaps made during the
sorting process. Before sorting a large array, it might be useful to test whether the array is already in sorted order and
thus does not need to be sorted. Procedures for testing if an array is sorted can be found on the IsArraySorted
page.

The code below in a implementation of the QSort algorithm for sorting an array. It will work with array that
contain either numeric or string values. The input array is sorted in place. That means that after the procedure
has ended, the original array will have been modified and sorted. The QSortInPlace and related
procedures will sort an array of numeric or string values in either ascending or descending order. The declaration
for QSortInPlace is shown below:

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

InputArray is the array to be sorted. LB is the first element of
the input array to sort. A value of -1 indicates to start sorting with the first element. UB
is the last element of the input array to sort. A value of -1 indicates to sort to the end of the array. By modifying
the values of LB and UB, you can sort only a subset of the array.
Descending, if False or omitted, causes the sort to progress in ascending order. If
Descending is True, the array is sorted in descending order. CompareMode
indicates whether the sorting is case sensitive or case insensitive. NoAlerts if True, supresses
error messages that may occur. The function returns True if the sort was successful or False if an error occurred.
There are several procedures that support the QSortInPlace function, so you should import the
entire module into your project.

ShortFadeBar

The code for QSortInPlace and supporting procedures is
shown below:

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

If LB = 0 Then
    CurMidpoint = ((LB + UB)  2) + 1
Else
    CurMidpoint = (LB + UB)  2 ' note integer division () here
End If
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
        ReverseArrayInPlace2 InputArray, LB, UB
    End If
End If

RecursionLevel = RecursionLevel - 1
QSortInPlace = True
End Function

Public 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

Public 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


Public 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
Dim OrigN As Long
Dim NewN As Long
Dim NewArr() As Variant

''''''''''''''''''''''''''''''''
' 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
            MsgBox "The input array multi-dimensional. ReverseArrayInPlace works only " & _
                   "on single-dimensional arrays."
        End If
        Exit Function

End Select

'''''''''''''''''''''''''''''''''''''''''''''
' Ensure that we have only simple data types,
' not an array of objects or arrays.
'''''''''''''''''''''''''''''''''''''''''''''
If IsSimpleDataType(InputArray(LBound(InputArray))) = False Then
    If NoAlerts = False Then
        MsgBox "The input array contains arrays, objects, or other complex data types." & vbCrLf & _
            "ReverseArrayInPlace can reverse only arrays of simple data types."
        Exit Function
    End If
End If

ReDim NewArr(LBound(InputArray) To UBound(InputArray))
NewN = UBound(NewArr)
For OrigN = LBound(InputArray) To UBound(InputArray)
    NewArr(NewN) = InputArray(OrigN)
    NewN = NewN - 1
Next OrigN

For NewN = LBound(NewArr) To UBound(NewArr)
    InputArray(NewN) = NewArr(NewN)
Next NewN

ReverseArrayInPlace = True
End Function


Public Function ReverseArrayInPlace2(InputArray As Variant, _
    Optional LB As Long = -1, Optional UB As Long = -1, _
    Optional NoAlerts As Boolean = False) As Boolean
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' ReverseArrayInPlace2
' This reverses the order of elements in InputArray. To reverse the entire array, omit or
' set to less than 0 the LB and UB parameters. To reverse only part of tbe array, set LB and/or
' UB to the LBound and UBound of the sub array to be reversed.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim N As Long
Dim Temp As Variant
Dim Ndx As Long
Dim Ndx2 As Long
Dim OrigN As Long
Dim NewN As Long
Dim NewArr() As Variant

''''''''''''''''''''''''''''''''
' Set the default return value.
''''''''''''''''''''''''''''''''
ReverseArrayInPlace2 = 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
            MsgBox "The input array multi-dimensional. ReverseArrayInPlace works only " & _
                   "on single-dimensional arrays."
        End If
        Exit Function

End Select

'''''''''''''''''''''''''''''''''''''''''''''
' Ensure that we have only simple data types,
' not an array of objects or arrays.
'''''''''''''''''''''''''''''''''''''''''''''
If IsSimpleDataType(InputArray(LBound(InputArray))) = False Then
    If NoAlerts = False Then
        MsgBox "The input array contains arrays, objects, or other complex data types." & vbCrLf & _
            "ReverseArrayInPlace can reverse only arrays of simple data types."
        Exit Function
    End If
End If

If LB < 0 Then
    LB = LBound(InputArray)
End If
If UB < 0 Then
    UB = UBound(InputArray)
End If

For N = LB To (LB + ((UB - LB - 1)  2))
    Temp = InputArray(N)
    InputArray(N) = InputArray(UB - (N - LB))
    InputArray(UB - (N - LB)) = Temp
Next N

ReverseArrayInPlace2 = True
End Function


Public Function IsSimpleNumericType(V As Variant) As Boolean
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' IsSimpleNumericType
' This returns TRUE if V is one of the following data types:
'        vbBoolean
'        vbByte
'        vbCurrency
'        vbDate
'        vbDecimal
'        vbDouble
'        vbInteger
'        vbLong
'        vbSingle
'        vbVariant if it contains a numeric value
' It returns FALSE for any other data type, including any array
' or vbEmpty.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If IsSimpleDataType(V) = True Then
    Select Case VarType(V)
        Case vbBoolean, _
                vbByte, _
                vbCurrency, _
                vbDate, _
                vbDecimal, _
                vbDouble, _
                vbInteger, _
                vbLong, _
                vbSingle
            IsSimpleNumericType = True
        Case vbVariant
            If IsNumeric(V) = True Then
                IsSimpleNumericType = True
            Else
                IsSimpleNumericType = False
            End If
        Case Else
            IsSimpleNumericType = False
    End Select
Else
    IsSimpleNumericType = False
End If
End Function

Public Function IsSimpleDataType(V As Variant) As Boolean
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' IsSimpleDataType
' This function returns TRUE if V is one of the following
' variable types (as returned by the VarType function:
'    vbBoolean
'    vbByte
'    vbCurrency
'    vbDate
'    vbDecimal
'    vbDouble
'    vbEmpty
'    vbError
'    vbInteger
'    vbLong
'    vbNull
'    vbSingle
'    vbString
'    vbVariant
'
' It returns FALSE if V is any one of the following variable
' types:
'    vbArray
'    vbDataObject
'    vbObject
'    vbUserDefinedType
'    or if it is an array of any type.

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Test if V is an array. We can't just use VarType(V) = vbArray
' because the VarType of an array is vbArray + VarType(type
' of array element). E.g, the VarType of an Array of Longs is
' 8195 = vbArray + vbLong.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If IsArray(V) = True Then
    IsSimpleDataType = False
    Exit Function
End If
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' We must also explicitly check whether V is an object, rather
' relying on VarType(V) to equal vbObject. The reason is that
' if V is an object and that object has a default proprety, VarType
' returns the data type of the default property. For example, if
' V is an Excel.Range object pointing to cell A1, and A1 contains
' 12345, VarType(V) would return vbDouble, the since Value is
' the default property of an Excel.Range object and the default
' numeric type of Value in Excel is Double. Thus, in order to
' prevent this type of behavior with default properties, we test
' IsObject(V) to see if V is an object.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
If IsObject(V) = True Then
    IsSimpleDataType = False
    Exit Function
End If
'''''''''''''''''''''''''''''''''''''
' Test the value returned by VarType.
'''''''''''''''''''''''''''''''''''''
Select Case VarType(V)
    Case vbArray, vbDataObject, vbObject, vbUserDefinedType
        '''''''''''''''''''''''
        ' not simple data types
        '''''''''''''''''''''''
        IsSimpleDataType = False
    Case Else
        ''''''''''''''''''''''''''''''''''''
        ' otherwise it is a simple data type
        ''''''''''''''''''''''''''''''''''''
        IsSimpleDataType = True
End Select

End Function

Public Function IsArrayAllocated(Arr As Variant) As Boolean
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' IsArrayAllocated
' Returns TRUE if the array is allocated (either a static array or a dynamic array that has been
' sized with Redim) or FALSE if the array has not been allocated (a dynamic that has not yet
' been sized with Redim, or a dynamic array that has been Erased).
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Dim N As Long

'''''''''''''''''''''''''''''''''''''''''''''''''''
' If Arr is not an array, return FALSE and get out.
'''''''''''''''''''''''''''''''''''''''''''''''''''
If IsArray(Arr) = False Then
    IsArrayAllocated = False
    Exit Function
End If

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Try to get the UBound of the array. If the array has not been allocated,
' an error will occur. Test Err.Number to see if an error occured.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
On Error Resume Next
N = UBound(Arr, 1)
If Err.Number = 0 Then
    '''''''''''''''''''''''''''''''''''''
    ' No error. Array has been allocated.
    '''''''''''''''''''''''''''''''''''''
    IsArrayAllocated = True
Else
    '''''''''''''''''''''''''''''''''''''
    ' Error. Unallocated array.
    '''''''''''''''''''''''''''''''''''''
    IsArrayAllocated = False
End If

End Function

ShortFadeBar

LastUpdate This page last updated: 7-June-2009.

Get our FREE VBA eBook of the 30 most useful Excel VBA macros.

Automate Excel so that you can save time and stop doing the jobs a trained monkey could do.

Claim your free eBook


VBA Code Snippets

At some point in your VBA life it will be necessary to display or re-order an array/list alphabetically (or reverse alphabetically).  Unfortunately, VBA doesn’t have a function for sorting arrays automatically, but with a little bit of coding we can create a reusable function which would achieve something similar.

Setting up the example

This first VBA code is to set the scene.  It creates an array, calls the function to sort the array and then outputs the sorted array.  You only need this first piece of code if you are following it through as an example.

Sub RunTheSortMacro()

Dim i As Long
Dim myArray As Variant

'Set the array
myArray = Array("p", "A", "G", 3, "l", "6", 10, "K", 7)

'myArray variable set to the result of SortArrayAtoZ function
myArray = SortArrayAtoZ(myArray)

'Output the Array through a message box
For i = LBound(myArray) To UBound(myArray)
    MsgBox myArray(i)
Next i

End Sub

Sort the array A-Z

I like to use a Function, rather than a Sub for sorting an array.  The function is a reusable piece of code, it can form part of your ‘Core’ module, which you can copy into any VBA project.  This function takes an array as its variable and outputs the same array sorted in alphabetical order.

Function SortArrayAtoZ(myArray As Variant)

Dim i As Long
Dim j As Long
Dim Temp

'Sort the Array A-Z
For i = LBound(myArray) To UBound(myArray) - 1
    For j = i + 1 To UBound(myArray)
        If UCase(myArray(i)) > UCase(myArray(j)) Then
            Temp = myArray(j)
            myArray(j) = myArray(i)
            myArray(i) = Temp
        End If
    Next j
Next i

SortArrayAtoZ = myArray

End Function

Sort the array Z-A

The reverse function to sort the array Z-A

Function SortArrayZtoA(myArray As Variant)

Dim i As Long
Dim j As Long
Dim Temp

'Sort the Array Z-A
For i = LBound(myArray) To UBound(myArray) - 1
    For j = i + 1 To UBound(myArray)
        If UCase(myArray(i)) < UCase(myArray(j)) Then
            Temp = myArray(j)
            myArray(j) = myArray(i)
            myArray(i) = Temp
        End If
    Next j
Next i

SortArrayZtoA = myArray

End Function

Headshot Round

About the author

Hey, I’m Mark, and I run Excel Off The Grid.

My parents tell me that at the age of 7 I declared I was going to become a qualified accountant. I was either psychic or had no imagination, as that is exactly what happened. However, it wasn’t until I was 35 that my journey really began.

In 2015, I started a new job, for which I was regularly working after 10pm. As a result, I rarely saw my children during the week. So, I started searching for the secrets to automating Excel. I discovered that by building a small number of simple tools, I could combine them together in different ways to automate nearly all my regular tasks. This meant I could work less hours (and I got pay raises!). Today, I teach these techniques to other professionals in our training program so they too can spend less time at work (and more time with their children and doing the things they love).


Do you need help adapting this post to your needs?

I’m guessing the examples in this post don’t exactly match your situation. We all use Excel differently, so it’s impossible to write a post that will meet everybody’s needs. By taking the time to understand the techniques and principles in this post (and elsewhere on this site), you should be able to adapt it to your needs.

But, if you’re still struggling you should:

  1. Read other blogs, or watch YouTube videos on the same topic. You will benefit much more by discovering your own solutions.
  2. Ask the ‘Excel Ninja’ in your office. It’s amazing what things other people know.
  3. Ask a question in a forum like Mr Excel, or the Microsoft Answers Community. Remember, the people on these forums are generally giving their time for free. So take care to craft your question, make sure it’s clear and concise.  List all the things you’ve tried, and provide screenshots, code segments and example workbooks.
  4. Use Excel Rescue, who are my consultancy partner. They help by providing solutions to smaller Excel problems.

What next?
Don’t go yet, there is plenty more to learn on Excel Off The Grid.  Check out the latest posts:

 

слэн

Пользователь

Сообщений: 5192
Регистрация: 16.01.2013

кроме как на листе, конечно..

 

Вообще есть куча разнообразных способов и методов… Вот еще один:

http://www.vbrussian.com/Article.asp?ID=97  

P.S. Google рулит)

 

слэн

Пользователь

Сообщений: 5192
Регистрация: 16.01.2013

а с использованием стандартных функций( Сишных, например) никто не видел?  

  я тоже надыбал одну ссылку по реализации алгоритма quicksort на VBA, но уже нашел там ошибку..

 

New

Пользователь

Сообщений: 4581
Регистрация: 06.01.2013

 

New

Пользователь

Сообщений: 4581
Регистрация: 06.01.2013

 

В VBA насколько я понимаю, ф-ции swap нету. Значит, напрямую преобразовать Си-пример в код VBA не получится. Вот в этой теме —

http://www.planetaexcel.ru/forum.php?thread_id=5274

(была указана выше) — Турбо-Ёж впроде бы предлагал подогнаный под Excel метод Fast Quick Sort.

 

Блин, до конца не разобрался) Swap — это ж отдельная процедура) Тогда пример Турбо-Ежа — точно Fast Quick Sort.

 

Потестил 2 способа Ежа и по ссылке

http://www.vbrussian.com/Article.asp?ID=97  

с помощью  
Sub TestSort()  
   Dim iEntry()  
   Dim j: j = 1000000  
   ReDim iEntry(1 To j)  
   For iOuter = LBound(iEntry()) To UBound(iEntry())  
       iEntry(iOuter) = Int((20 * Rnd) — 10)  
   Next  
  Start1 = Timer  
  Call ShellSort2(iEntry())  
  MsgBox Format((Timer — Start1), «00:00»)  
End Sub  

  Так вот, сортировка Ежа на миллионе случайных чисел — 21 секунда, вторая сортировка — 7 секунд..  
Кстати, может кто-нибудь знает алгоритм извлечения уникальных элементов,кроме последовательного перебора отсортированного массива

 

слэн

Пользователь

Сообщений: 5192
Регистрация: 16.01.2013

скачал алгоритм qsort с

www.cpearson.com  

только там в «NumberOfArrayDimensions» надо заменить как минимум  
Dim Res As Integer    на           Dim Res As long  
а лучше вообще так:  
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 Long  
On Error GoTo ex  
‘ 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  
   If UBound(arr, Ndx) Then:  
Loop  

   ex: NumberOfArrayDimensions = Ndx — 1  

   End Function  

  а так довольно быстро работает.

 

слэн

Пользователь

Сообщений: 5192
Регистрация: 16.01.2013

{quote}{login=Артем}{date=09.01.2009 07:35}{thema=}{post}Потестил 2 способа Ежа и по ссылке

http://www.vbrussian.com/Article.asp?ID=97  

с помощью  
Sub TestSort()  
   Dim iEntry()  
   Dim j: j = 1000000  
   ReDim iEntry(1 To j)  
   For iOuter = LBound(iEntry()) To UBound(iEntry())  
       iEntry(iOuter) = Int((20 * Rnd) — 10)  
   Next  
  Start1 = Timer  
  Call ShellSort2(iEntry())  
  MsgBox Format((Timer — Start1), «00:00»)  
End Sub  

  Так вот, сортировка Ежа на миллионе случайных чисел — 21 секунда, вторая сортировка — 7 секунд..  
Кстати, может кто-нибудь знает алгоритм извлечения уникальных элементов,кроме последовательного перебора отсортированного массива{/post}{/quote}  

    по извлечению уникальных элементов посмотрите здесь:

http://sql.ru/forum/actualthread.aspx?bid=46&tid=333152&pg=-1  

сортировать не надо..  
лучший способ с scripting.dictionary  
но и с коллекциями очень неплохо..  

  на миллионе не пробовал, но на 60000(в 2003 икселе) доли секунды..

 
 

New

Пользователь

Сообщений: 4581
Регистрация: 06.01.2013

В приложенном архиве 3 файла  

  1) сортировка от Chip Pearson, которую дал Слэн  
2) сортировка по методу Шелла с сайта

http://www.vbrussian.com/Article.asp?ID=97  

3) сортировка взятая отсюда

http://www.vbnet.ru/forum/show.aspx?id=90781&page=1  

  По моим результатам 3-й вариант самый быстрый. Если я не прав, поправьте меня.

 

ZVI

Пользователь

Сообщений: 4328
Регистрация: 23.12.2012

Добрый вечер, Слэн! и всем ;-)  

  Алгоритмов VBA-сортировки опубликовано достаточно много.    
Прилагаю небольшой архивчик методов сортировки, когда-то надёрганных из Интернета.  

  Вот здесь хороший ресурс по методам сортировки на нескольких языках программирования:  

http://alglib.sources.ru/sorting/  

Там же есть и статья по сравнению эффективности алгоритмов сортировки массивов:  

http://alglib.sources.ru/articles/sort.php  

Только советую творчески отнестись к коду и оценкам, так как из-за универсальности код не оптимизирован. И на VB (VBA) можно получить иные результаты эффективности.  
Кстати, на этом же сайте приведено множество интересных методов матобработки.  

  Эффективность сортировки  сильно зависит от размера массива, типа данных, метода сортировки, компилятора и/или среды программирования. В частности, по моим оценкам, на обработках длинных строк при прочих равных условиях  компилятор Borland C++ в 2 … 2.5 раза быстрее компилятора Visual C++.  Но и функциями VBA можно написать код, который обрабатывает строки в несколько раз быстрее, чем некоторые другие встроенные VBA-функции, написанные на C(++), если вместо автоматизации по скорости компилятора для конкретной задачи написать более оптимальный код, понимая, где реальные тормоза: memory reallocation, adjusting of addresses etc.  

   Известны также  методы сортировки коллекций, но они неэффективны на больших массивах:    

http://www.dailydoseofexcel.com/archives/2004/06/02/sort-a-collection/  
http://www.freevbcode.com/ShowCode.asp?ID=3645  

Впрочем, использование дополнительного индексного массива заслуживает внимания:  

http://www.source-code.biz/snippets/vbasic/  

Индексный массив особенно насущен и для сортировки многомерных массивов.  

  На мой взгляд, в дополнение к использованию индексного массива представляет интерес свопинг не самих переменных, а их адресов с помощью API CopyMemory  (RtlMoveMemory). Подозреваю, что Excel как и поступает, но реальных тестов эффективности этого метода я не проводил.

 

согласен с Павлом, метод с Vbnet самый быстрый — на моем ПК миллион значений обрабытывает за 5 сек, пирсоновский аж 34 секунды.(Не смотрел пока то что выложил ZVI)

 

метод бинарных деревьев(при прочих равных) — 6,6 секунд однако…

 

New

Пользователь

Сообщений: 4581
Регистрация: 06.01.2013

Хм, а кто знает, как запустить этот метод бинарных деревьев с одномерным массивом? ))  

  Объявил массив  

  Dim MyArr() As Double, i As Long  

     ReDim MyArr(0 To 50000)  
   For i = 0 To 50000  
       MyArr(i) = Round(Rnd * 50000, 0)  
   Next i  

    Вызываю процедуру сортировки    

  HeapSort MyArr(), 1    

  1 — т.е. одномерный массив (в процедуре написано указывать размерность)  

  но процедура сразу заканчивается, т.к. в начале процедуры написано    

     If N = 1# Then  
       Exit Sub  
   End If  

  С размерностью 0 — выходит ошибка (ну, это логично)  
с размерностю 2 — сортировка не происходит.    

  Файл прилагаю.

 

New

Пользователь

Сообщений: 4581
Регистрация: 06.01.2013

ZVI, в твоём архиве в файле Sort_Routines.xls (где представлены различные методы сортировки) в модуле modHeapSort (метод бинарных деревьев) неправильно объявлены многие переменные, типа  

  Dim base, n, nn, i, m As Long  
Dim base, nDiv2, i, k As Long  

  При таком объявлении, при сортировке 50000 целых чисел у меня был лучший результат 1500 мс, при правильном объявлении всех этим переменных лучшим результатом стало 1047 мс, т.е. почти 500 мс выигрыш.  

  Я понимаю, это камень не в твой огород, а в огород автора файла.  

  Но всё равно очень долго работает этот метод бинарных деревьев  
— сперва циклом заполняется массив значениями с листа  
— почему-то долго сортируется  
— циклом долго выводим результат обратно на лист  

  Кстати, в этом же файле в модуле modMain, автор файла явно пишет, что он предпочитает из всех методов QuickSort, вот эта строка  

  varray2 = modQuickSort.Quicksort(varray) ‘Preferred Method of Sorting  

  Ох, что-то я не пойму. Все говорят, что метод бинарных деревьев самый быстрый, но при тестировании он почему-то самый долгий ))  

  Может у меня руки кривые? Разъясни, плиз )  

  Файл с мет. Бинар. деревьев из твоего файла прилагаю.  

    Я пытаюсь найти самый максимально быстрый способ сортировки массивов на листе Excel, как текстовых, так и числовых.

 

New

Пользователь

Сообщений: 4581
Регистрация: 06.01.2013

Потестировал я сортировку QuickSort из файла Sort_Routines.xls, которую так полюбил автор этого файла. При тестировании я чуть не умер… лучший результат … эм вам в миллисекундах сказать или лучше в минутах? )) В общем 252312 мс, на сколько я понимаю, это 4,2 минуты.    

  Файл прикладываю.  

  На сколько я понимаю, всё-таки самый быстрый пока остаётся 3-й вариант из моего поста post_44028.xls (

http://www.planetaexcel.ru/docs/forum_upload/post_44028.rar

), который я выложил выше в этой теме. Так?  

  так… 5 утра … надо ложиться спать. Всем споки.

 

New

Пользователь

Сообщений: 4581
Регистрация: 06.01.2013

По моему посту выше, где я не мог запустить сортировку с методом бинарных деревьев. Я просто неправильно прочитал название аргумента. Надо читать N — размер массива, а я прочитал «размерность массива» — из-за этого не мог запустить процедуру.  

  В общем, я сегодня потестировал две версия сортировки методом бинарных деревьев. Оба они оказались не самыми лучшими. Или я, может, что-то делаю не так.  

  Как я уже и сказал, пока лучшим остаётся 3-й вариант файла в посте    

http://www.planetaexcel.ru/docs/forum_upload/post_44028.rar  

  2 файла с бинарными деревьями выкладываю на вашего тестирования и анализа  

  а я спать, всем споки

 

ZVI

Пользователь

Сообщений: 4328
Регистрация: 23.12.2012

Насчет оценок методов я предупреждал :) в VBA результаты методов иные, чем на C++. Коды, что приложил в архиве, не мои, рассматривайте их просто как (до)исторические :-)  
А метод, выбранный Павлом, действительно работает очень шустро.    
Да и сам Павел поработал этой ночкой очень плодотворно, за что ему большое спасибо.    
Интересно было бы, конечно, достичь в VBA скорости сортировки Excel-я.  
А то в определенных случаях все же приходится сортировать в ячейках.

 

слэн

Пользователь

Сообщений: 5192
Регистрация: 16.01.2013

да, вариант qsort без рекурсии — лучший, но почти в два раза медленнее икселевской сортировки  

  непонятки:  

  когда вызываю как процедуру, т.е например: call qsort(arr)  
отсортированный  массив не передается обратно в процедуру и , соответственно, не отображается на листе..  

  если же вызывать как функцию: if qsort(arr) then:  
то все нормально.  

  и второе — оправдано ли оформление функцией простых действий? типа swap — в одном месте в коде употребляется  — не проще ли прямо там и записать алгоритм swap?  
в другом месте — сравнение двух величин оформлено функцией.. я понимаю, когда пишется универсальный код, заранее неизвестны типы данных, тогда в процедуру сортировки передается УКАЗАТЕЛЬ на функцию сравнения, кот вы выбираете сами(или можно выбирать автоматически, но это хуже).  

  хотя, конечно, это и несущественное замедление.. но если таких функций насовать туда поболе..

 

ZVI

Пользователь

Сообщений: 4328
Регистрация: 23.12.2012

В варианте Павла, работает и  Call QuickSortNonRecursive(Rng) и просто QuickSortNonRecursive Rng.  

  Насчет swap все правильно: для уменьшения времени лучше явно вписать перестановку в общую процедуру, чем многократно вызывать функцию. Сам вызов функции на любом языке программирования — это время, не говоря уже о времени на создание локальных переменных внутри функции при каждом вызове и еще, возможно, на приведение типов данных.  
Например, в С, когда нужна скорость, вместо вызова функций используют макросы типа #define isdigit(x) ((x) >= ‘0’ && (x) <= ‘9’), которые компилятор подсовывает в каждое место, откуда вызывался макрос. Но, с другой стороны, это увеличивает размер кода.  
В VBA тоже код увеличивается в размере, если подстановку (swap) потребуется вписать в несколько мест, зато сэкономится время тем большее, чем больше обрабатывемый массив.

 

New

Пользователь

Сообщений: 4581
Регистрация: 06.01.2013

Может кто-нибудь изменит макрос (QuickSort без рекурсии), как вы считаете будет быстрее, т.е. swap вставите внутрь процедуры? Чтобы, как говорится, вышла максимально быстрая процедура? (а то у меня что-то голова сегодня не работает)

 

слэн

Пользователь

Сообщений: 5192
Регистрация: 16.01.2013

крутил-крутил.. кроме swap ничего улучшить не получается. ну памяти чуть сэкономил, но и это еще вопрос :)  
Public Sub QuickSortNonRecursive(SortArray As Variant)  
Dim i As Long, j As Long, lb As Long, ub As Long  
Dim stack() As QuickStack, stackpos As Long, ppos As Long, pivot As Variant, swp  
   ReDim stack(1 To 64)  
   stackpos = 1  

     stack(1).Low = LBound(SortArray)  
   stack(1).High = UBound(SortArray)  
   Do  
       ‘Взять границы lb и ub текущего массива из стека.  
       lb = stack(stackpos).Low  
       ub = stack(stackpos).High  
       stackpos = stackpos — 1  
       Do  
           ‘Шаг 1. Разделение по элементу pivot  
           ppos = (lb + ub) 2  
           i = lb: j = ub: pivot = SortArray(ppos)  
           Do  
                While SortArray(i) < pivot: i = i + 1: Wend  
                While pivot < SortArray(j): j = j — 1: Wend  
                If i <= j Then  
                    swp = SortArray(i): SortArray(i) = SortArray(j): SortArray(j) = swp  
                    i = i + 1  
                    j = j — 1  
               End If  
          Loop While i <= j  

             ‘Сейчас указатель i указывает на начало правого подмассива,  
           ‘j — на конец левого lb ? j ? i ? ub.  
           ‘Возможен случай, когда указатель i или j выходит за границу массива  
           ‘Шаги 2, 3. Отправляем большую часть в стек  и двигаем lb,ub  

             If i < ppos Then   ‘правая часть больше  
               If i < ub Then  
                   stackpos = stackpos + 1  
                   If stackpos > UBound(stack) Then ReDim Preserve stack(1 To UBound(stack) + 32)  
                   stack(stackpos).Low = i  
                   stack(stackpos).High = ub  
               End If  
               ub = j        ‘следующая итерация разделения будет работать с левой частью  
           Else  
               If j > lb Then  
                   stackpos = stackpos + 1  
                   If stackpos > UBound(stack) Then ReDim Preserve stack(1 To UBound(stack) + 32)  
                   stack(stackpos).Low = lb  
                   stack(stackpos).High = j  
               End If  
               lb = i  
           End If  
       Loop While lb < ub  
   Loop While stackpos  
End Sub

 

New

Пользователь

Сообщений: 4581
Регистрация: 06.01.2013

Да, действительно быстрее. Спасибо, Слэн.  

  Итак, выкладываю 2 варианта  
1) Самый быстрый для текста  
2) Самый быстрый для чисел  

  Они оба основаны на сортировке QuickSort без рекурсий, но с разными типами переменных в функции Swap (в одном случае переменные типа Variant, в другом String)  

  P.S. Совместить два в одном, чтобы макрос быстро обрабатывал и текст и числа. У меня не вышло.

 

ZVI

Пользователь

Сообщений: 4328
Регистрация: 23.12.2012

Павел, у Слэна как раз и сделан вариант как для строк, так и для чисел.  
Только если вместо:  
Public Sub QuickSortNonRecursive(SortArray As Variant)  
написать:  
Public Sub QuickSortNonRecursive(SortArray() As Variant)  
то будет примерно в 1.6 раза быстрее.  
Всего лишь 2 скобки добавили :-)  

  В приложении немного переделанный файл для совместной сортировки строк и чисел.

 

слэн

Пользователь

Сообщений: 5192
Регистрация: 16.01.2013

вот так вот :)  спасибо , научили..  

  осталось чуть-чуть

 

слэн

Пользователь

Сообщений: 5192
Регистрация: 16.01.2013

интересно, ZVI, почему именно умножать на 2 при увеличении массива stack?  

  это не слишком? ведь выход за пределы массива вовсе не обозначает, что потребуется еще столько же уровней, скорее наоборот.. можно даже предположить сколько понадобится по формуле:  
например, для                 If i < ub Then  
                   stackpos = stackpos + 1  

  можно оценить как:  (ub-i)/(ub-lb)*stackpos  

  да и на самом деле, для случайных чисел , количество уровней колеблется в пределах: 10-11  
а при изначально упорядоченном массиве, возрастает лишь до 14  

  на массиве из 65000 значений  

  ps  не лень вам ждать каждый раз пока массив создается? да икак в таком случае сравнивать методы?  

  свой файл прилагаю. перед первой сортировкой(ну и , соответственно, по желанию) нажать «поменять массив»

 

слэн

Пользователь

Сообщений: 5192
Регистрация: 16.01.2013

#30

11.01.2009 15:56:31

или умножение просто быстрее сложения?

Живи и дай жить..

Понравилась статья? Поделить с друзьями:
  • Excel vba обновить запросы
  • Excel vba массивы присвоение значений
  • Excel vba обновить запрос
  • Excel vba обновить все подключения
  • Excel vba массивы данных