如何在 excel vba 中获取数组中的元素子集?

问题描述 投票:0回答:3

如何在 excel vba 中获取数组中的第 3 到第 6 个元素?像下面这样的东西。

    Dim x(9) as variant, y(3) as variant

    y(0:3) = x(2:5)
vba excel
3个回答
5
投票

在 VBA 中,不像在 python 中,我们不能直接“子集化”一个数组。

我们只能通过指定

i
j
在某些边界之间来循环感兴趣的索引,例如
i
在第 1 行和第 2 行之间,
j
在第 2 列和第 3 列之间。当然,我们也可以直接按位置索引到数组中,例如
arr(1)
i
只是一个代表行索引的变量,
j
列索引。

或者,我们可以使用

Index
来“切出”特定的行或列;我猜你可能会称之为子集化,但从你的语法来看,我认为你正在以 python 的方式思考。

Application.WorksheetFunction.Index(array,n, 0)
将行
n
切出数组

Application.WorksheetFunction.Index(array, 0, n)
将列
n
切出数组

Option Explicit

Public Sub ArrayStuff()
    Dim arr(0 To 5) As Long, i As Long
    For i = 0 To 5
        arr(i) = i
    Next

    'Loop only required indices
    For i = 2 To 3
        Debug.Print arr(i)
    Next

    'Slice via Application.WorksheetFunction.Index
    Dim arr2(0 To 2, 0 To 2) As Long, j As Long, counter As Long

    For i = LBound(arr2, 1) To UBound(arr2, 1) '<==  here can specify particular rows
        For j = LBound(arr2, 2) To UBound(arr2, 2) '<== here can specify particular columns
            counter = counter + 1
            arr2(i, j) = counter
        Next
    Next

    MsgBox Join(Application.WorksheetFunction.Transpose(Application.WorksheetFunction.Index(arr2, 0, 1)), ",") 'slice a column out
    MsgBox Join(Application.WorksheetFunction.Index(arr2, 1, 0), ",") 'slice a row out
    MsgBox Join(Application.WorksheetFunction.Index(arr2, 2, 0), ",") 'slice a row out
End Sub

2
投票

对于不想使用循环或范围方法处理此问题的任何人, 这是一个经过测试的解决方案,用于处理数组的子集(不需要是整行或整列):

Application.Sum(Application.Index(Array(1, 2, 3, 4, 5), 0, Evaluate("ROW(2:5)")))

对数组的第 2 到第 5 个元素 (1, 2, 3, 4, 5) 求和。 对于二维数组,只需执行以下操作:

Application.Average(.Index(DataArray, Evaluate("ROW(17:21)"), 5))

将计算二维数组“DataArray”的五个元素子集(第 17 行到 21 行,第 5 列)的平均值。

顺便说一句,我还没有尝试评估这种方法的效率,但它确实是一种解决方法。希望这会有所帮助。


0
投票

Python 切片克隆

我最近想要您在示例中提供的相同功能,它基本上模仿了 Python 的 slice 方法——所以我决定编写代码来做到这一点。

随意阅读我的代码注释以了解其工作原理,但它应该与 pythons

[start:stop:step]
格式相同。

请注意,我写这篇文章时希望将基数设置为 0,因此如果您的基数不同,则可能需要先增强代码。

' The Slice function takes an input array and returns a new array
' that contains a subset of the elements from the input array.
'
' The subset is defined by the `startIndex`, `stopIndex`, and `stepCount`
' parameters.
'
' The `startIndex` parameter specifies the index of the first
' element to include in the result. If `startIndex` is negative,
' it counts from the end of the input array.
'
' The `stopIndex` parameter specifies the index of the first element
' to exclude from the result. If `stopIndex` is negative, it counts
' from the end of the input array.
'
' The `stepCount` parameter specifies how many elements to skip
' between each element in the result. For example, if `stepCount`
' is 2, every other element will be included in the result. If
' `stepCount` is negative then it works in reverse order.
'
' @author Robert Todar <[email protected]>
Public Function Slice( _
    ByVal arr As Variant, _
    Optional ByVal startIndex As Long = 0, _
    Optional ByVal stopIndex As Long = 0, _
    Optional ByVal stepCount As Long = 1 _
) As Variant
    Dim result As Variant
    Dim currentIndex As Long
    Dim resultIndex As Long

    If stepCount = 0 Then
        Err.Raise 5, "Slice", "Step count must be greater than or less than zero."
        Exit Function
    End If

    ' Handle negative indices for startIndex and stopIndex by
    ' converting them to positive indices.
    If startIndex < 0 Then startIndex = UBound(arr) + 1 + startIndex
    If stopIndex < 0 Then stopIndex = UBound(arr) + 1 + stopIndex

    If stepCount > 0 Then
        ' Calculate the stop index if it is not provided (i.e., if it is zero).
        If stopIndex = 0 Then stopIndex = UBound(arr) + 1
        
        ' Calculate the size of the result array based on
        ' start/stop indices and step count.
        resultIndex = (stopIndex - startIndex) \ stepCount - 1
        
        ' Adjust the stopIndex as it is excluded from the results.
        ' This is done after getting the resultIndex as the array
        ' is base 0 and will still need to account for that 0 index.
        stopIndex = stopIndex - 1

    Else
        ' If no input is provided then the start and
        ' stops need to get flipped from the start to
        ' the end.
        If startIndex = 0 Then startIndex = UBound(arr)
        If stopIndex = 0 Then stopIndex = -1

        ' Need to add 1 to the stopindex as it needs to
        ' be excluded from the results. In this senerio,
        ' this is done before the resultIndex is calculated as
        ' we don't have to account for the 0 index.
        stopIndex = stopIndex + 1
        resultIndex = (startIndex - stopIndex) \ Abs(stepCount)
    End If
    
    ' Create a new array with appropriate size to store results.
    ' and get the starting result index. This will be incremented 
    ' each iteration.
    ReDim result(0 To resultIndex)
    resultIndex = LBound(result)

    ' Fill in values for resulting array using start/stop indices
    ' and step count as specified by user inputs.
    For currentIndex = startIndex To stopIndex Step stepCount
        result(resultIndex) = arr(currentIndex)
        resultIndex = resultIndex + 1
    Next currentIndex
    
    ' Return resulting sliced array back to user.
    Slice = result
End Function

演示

这是一个演示,展示了 python 方法与我的版本的比较。

Sub DemoSliceFunction()
    Dim a As Variant
    a = Array(0, 1, 2, 3, 4, 5)

    Debug.Print "Starting Data", Join(a)
    Debug.Print "Python Example: [::]", Join(Slice(a))
    Debug.Print "Python Example: [1:]", Join(Slice(a, 1))
    Debug.Print "Python Example: [-1:]", Join(Slice(a, -1))
    Debug.Print "Python Example: [-2:]", Join(Slice(a, -2))
    Debug.Print "Python Example: [:4]", Join(Slice(a, , 4))
    Debug.Print "Python Example: [1:4]", Join(Slice(a, 1, 4))
    Debug.Print "Python Example: [:-3]", Join(Slice(a, , -3))
    Debug.Print "Python Example: [1:-2]", Join(Slice(a, 1, -2))
    Debug.Print "Python Example: [-3:-2]", Join(Slice(a, -3, -2))
    Debug.Print "Python Example: [::2]", Join(Slice(a, , , 2))
    Debug.Print "Python Example: [::-1]", Join(Slice(a, , , -1))
    Debug.Print "Python Example: [-3::-1]", Join(Slice(a, -3, , -1))
    Debug.Print "Python Example: [-4::-1]", Join(Slice(a, -4, , -1))
    Debug.Print "Python Example: [:-3:-1]", Join(Slice(a, , -3, -1))
    Debug.Print "Python Example: [:-4:-1]", Join(Slice(a, , -4, -1))
    Debug.Print "Python Example: [-2:-5:-1]", Join(Slice(a, -2, -5, -1))
End Sub

测试

另外,我已经提供了这个方法的测试脚本。

Sub TestSliceFunction()
    Dim a As Variant
    a = Array(0, 1, 2, 3, 4, 5)

    Debug.Assert Join(a) = "0 1 2 3 4 5"
    Debug.Assert Join(Slice(a)) = "0 1 2 3 4 5"
    Debug.Assert Join(Slice(a, 1)) = "1 2 3 4 5"
    Debug.Assert Join(Slice(a, -1)) = "5"
    Debug.Assert Join(Slice(a, -2)) = "4 5"
    Debug.Assert Join(Slice(a, , 4)) = "0 1 2 3"
    Debug.Assert Join(Slice(a, 1, 4)) = "1 2 3"
    Debug.Assert Join(Slice(a, , -3)) = "0 1 2"
    Debug.Assert Join(Slice(a, 1, -2)) = "1 2 3"
    Debug.Assert Join(Slice(a, -3, -2)) = "3"
    Debug.Assert Join(Slice(a, , , 2)) = "0 2 4"
    Debug.Assert Join(Slice(a, , , -1)) = "5 4 3 2 1 0"
    Debug.Assert Join(Slice(a, -3, , -1)) = "3 2 1 0"
    Debug.Assert Join(Slice(a, -4, , -1)) = "2 1 0"
    Debug.Assert Join(Slice(a, , -3, -1)) = "5 4"
    Debug.Assert Join(Slice(a, , -4, -1)) = "5 4 3"
    Debug.Assert Join(Slice(a, -2, -5, -1)) = "4 3 2"

    Debug.Print "TestSliceFunction: PASS"
End Sub
© www.soinside.com 2019 - 2024. All rights reserved.