VBA: Reverse an array?

自闭症网瘾萝莉.ら 提交于 2020-01-05 04:07:13

问题


How could I reverse an array that is full of integers e.g.:

[1;5;8;45;54]

To:

[54;45;8;5;1]

Are there any built in functions I could use?

I tried using this method:

Array.Reverse(arr)

I added Mscorlib.dll from Tools > References, but it showed error: Syntax error. At the Array.Reverse(arr) location.


回答1:


Array.Reverse sounds like VB.Net, not VBA.

Chip Pearson has functions for just about anything you will want to do with arrays (and other structures).

http://www.cpearson.com/excel/vbaarrays.htm --> ReverseArrayInPlace

The relevant part is:

Ndx2 = UBound(InputArray)
' loop from the LBound of InputArray to the midpoint of InputArray
For Ndx = LBound(InputArray) To ((UBound(InputArray) - LBound(InputArray) + 1) \ 2)
    'swap the elements
    Temp = InputArray(Ndx)
    InputArray(Ndx) = InputArray(Ndx2)
    InputArray(Ndx2) = Temp
    ' decrement the upper index
    Ndx2 = Ndx2 - 1
Next Ndx



回答2:


you could use ArrayList class and wrap its Reverse method:

Function ReverseArray(arr As Variant) As Variant
    Dim val As Variant

    With CreateObject("System.Collections.ArrayList") '<-- create a "temporary" array list with late binding 
        For Each val In arr '<--| fill arraylist
            .Add val
        Next val
        .Reverse '<--| reverse it
        ReverseArray = .Toarray '<--| write it into an array
    End With
End Function

to be used like:

Sub main()
    Dim arr As Variant

    arr = ReverseArray(Array(1, 2, 3, 4, 5)) '<-- it returns an array of Variant/Integer with values 5,4,3,2,1        
End Sub



回答3:


Andre's answer referring to Chip Pearson's function I believe the +1 in the for loop is in error that in cases of LBound and UBound not BOTH being EVEN or BOTH being ODD results in the mid point reversal being reverted. i.e. the difference between LBound and UBound being ODD.

Consider 0 = LBound and 9 = UBound.

9 + 1 = 10 / 2 = 5

So the loop will be for Ndx = 0 to 5. That is 6 iterations. One iteration too many.

Results in the following swaps.
Ndx = 0, Ndx2 = 9: 0<>9
Ndx = 1, Ndx2 = 8: 1<>8
Ndx = 2, Ndx2 = 7: 2<>7
Ndx = 3, Ndx2 = 6: 3<>6
Ndx = 4, Ndx2 = 5: 4<>5
Ndx = 5, Ndx2 = 4: 5<>4

So the mid point elements 4 and 5 are swapped, then swapped back.
Resulting in the order of: 9,8,7,6,4,5,3,2,1,0

Also LBound should be added to the UBound, not subtracted. If subtracted then it only works for LBound of zero. Consider 50 = LBound, 100 = UBound. That would result in For Ndx = 50 to 25. Note, this is supposed to be a FROM, TO calculation not an iterations count calculation.

Here is my functions for reversing one and two dimensional arrays.
They are also able to optionally retain a specified number of header rows.

' Reverse array (one dimensional), optionally retain header rows.
Private Sub Reverse_Array_1d(ByRef Ary As Variant, Optional Header_Rows As Integer = 0)

 Dim Dimension_Y As Integer     ' Rows (height)
 Dim Y_first As Long
 Dim Y_last As Long
 Dim Y_last_plus_Y_first As Long
 Dim Y_next As Long

 Dimension_Y = 1
 Y_first = LBound(Ary, Dimension_Y) + Header_Rows
 Y_last = UBound(Ary, Dimension_Y)
 Y_last_plus_Y_first = Y_last + Y_first

 Dim tmp As Variant

 For Y = Y_first To Y_last_plus_Y_first / 2
    Y_next = Y_last_plus_Y_first - Y
    tmp = Ary(Y_next)
    Ary(Y_next) = Ary(Y)
    Ary(Y) = tmp
 Next

End Sub

ReDim Ary(0 To 9) As Variant
Header_Rows = 1
Call Reverse_1d_Array(Ary, CInt(Header_Rows))

 

' Reverse array (two dimensional), optionally retain header rows.
Private Sub Reverse_Array_2d(ByRef Ary As Variant, Optional Header_Rows As Integer = 0)

 Dim Dimension_Y As Integer     ' Rows (height)
 Dim Y_first As Long
 Dim Y_last As Long
 Dim Y_last_plus_Y_first As Long
 Dim Y_next As Long

 Dimension_Y = 1
 Y_first = LBound(Ary, Dimension_Y) + Header_Rows
 Y_last = UBound(Ary, Dimension_Y)
 Y_last_plus_Y_first = Y_last + Y_first

 Dim Dimension_X As Integer      ' Columns (width)
 Dim X_first As Long
 Dim X_last As Long

 Dimension_X = 2
 X_first = LBound(Ary, Dimension_X)
 X_last = UBound(Ary, Dimension_X)

 ReDim tmp(X_first To X_last) As Variant

 For Y = Y_first To Y_last_plus_Y_first / 2
    Y_next = Y_last_plus_Y_first - Y
    For X = X_first To X_last
        tmp(X) = Ary(Y_next, X)
        Ary(Y_next, X) = Ary(Y, X)
        Ary(Y, X) = tmp(X)
    Next
 Next

End Sub

ReDim Ary(0 To 9, 0 To 3) As Variant
Header_Rows = 1
Call Reverse_2d_Array(Ary, CInt(Header_Rows))



回答4:


(1): The simple but limited solution, if your data is made of of either single characters or palindromes. Let's assume your array is named vTmp, and you can pick a delimiter that doesn't appear in your data -- I'll use "|". Then a one-line approach is:

vTmp = VBA.Split(VBA.StrReverse(VBA.Join(vTmp, "|")), "|")

It is limited, because each value will be reversed as well. It could be solved by replacing the values with indexes that refer to values in another array, but then it's no simpler than the other solutions.

(2): The full solution, as complete Excel VBA function:

Public Function rxReverse(uInput) As Variant
  '' Simply reverses the order of items in input array.
  '' If Input is singular, then it reverses the content.
  ''
  '' v1 Rich Sulin 05-08-2019
  Dim vTmp As Variant
  Dim a1 As Long, a2 As Long, a3 As Long
  Dim i As Long, j As Long
    ''
    rxReverse = vbNullString
    ''
    '' uInput is a Range object?
    If TypeOf uInput Is Range Then
        a1 = 1
        If uInput.Columns.Count > 1 Then
            a2 = uInput.Columns.Count
            ReDim vTmp(a1 To a2) As Variant
            a3 = 1 + (a2 - a1) / 2
            For i = a1 To a3
                j = a2 - i + 1
                vTmp(j) = uInput(1, i).Value
                vTmp(i) = uInput(1, j).Value
            Next i
        ElseIf uInput.Rows.Count > 1 Then
            a2 = uInput.Rows.Count
            ReDim vTmp(a1 To a2) As Variant
            a3 = 1 + (a2 - a1) / 2
            For i = a1 To a3
                j = a2 - i + 1
                vTmp(j) = uInput(1, i).Value
                vTmp(i) = uInput(1, j).Value
            Next i
        Else
            vTmp = VBA.StrReverse(VBA.CStr(uInput.Value))
        End If
    ''
    '' uInput is an Array?
    Else
        Select Case VBA.VarType(uInput)
            Case Is >= vbArray
                a1 = LBound(uInput)
                a2 = UBound(uInput)
                ReDim vTmp(a1 To a2) As Variant
                a3 = 1 + (a2 - a1) / 2
                For i = a1 To a3
                    j = a2 - i + 1
                    vTmp(j) = uInput(i)
                    vTmp(i) = uInput(j)
                Next i
    ''
    '' uInput is an irrelevant type?
            Case vbNull, vbEmpty, vbError
                Exit Function
    ''
    '' uInput is a singular data; reverse it.
            Case Else
                vTmp = VBA.StrReverse(VBA.CStr(uInput))
        End Select
    End If
    ''
    rxReverse = vTmp
    ''
    '' If called from a Worksheet, vertically, then orient output array "vertically"
    If VBA.IsObject(Application.Caller) Then
        If Application.Caller.Rows.Count > 1 Then
            rxReverse = Application.WorksheetFunction.Transpose(vTmp)
        End If
    End If
End Function


来源:https://stackoverflow.com/questions/40563940/vba-reverse-an-array

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!