【问题标题】:Dynamicaly change the nr. of dimensions of a VBA array动态更改 nr。 VBA 数组的维数
【发布时间】:2018-11-06 02:56:38
【问题描述】:

我想知道是否有任何方法可以更改数组的维数:

  1. 在 VBA 中,
  2. 取决于整数max_dim_bound,它表示 所需的编号。尺寸。
  3. 允许维度的起始索引:E.G. `array(4 to 5, 3 to 6) 其中3到6的个数是可变整数。

  4. *在代码本身没有额外的工具

  5. *不导出代码。

需要明确的是,以下更改不会更改数组维度的 nr(仅是每个维度中元素的起始结束索引):

my_arr(3 to 5, 6 to 10) 
'changed to:
my_arr(4 to 8, 2 to 7)

以下示例将成功更改 nr。数组中的维数:

my_arr(3 to 5, 6 to 10) 
'changed to:
my_arr(4 to 8, 2 to 7,42 to 29)

这也将是 nr 的变化。数组中的维数:

my_arr(4 to 8, 2 to 7,42 to 29)
'changed to:
my_arr(3 to 5, 6 to 10) 

到目前为止,我的尝试包括:

Sub test_if_dynamically_can_set_dimensions()
    Dim changing_dimension() As Double
    Dim dimension_string_attempt_0 As String
    Dim dimension_string_attempt_1 As String
    Dim max_dim_bound As String
    Dim lower_element_boundary As Integer
    Dim upper_element_boundary As Integer

    upper_element_boundary = 2
    max_dim_bound = 4

    For dimen = 1 To max_dim_bound
        If dimen < max_dim_bound Then
            dimension_string_attempt_0 = dimension_string_attempt_0 & "1 To " & upper_element_boundary & ","
            MsgBox (dimension_string_attempt_0)
        Else
            dimension_string_attempt_0 = dimension_string_attempt_0 & "1 To " & upper_element_boundary
        End If
    Next dimen
    MsgBox (dimension_string_attempt_0)
    'ReDim changing_dimension(dimension_string_attempt_0) 'does not work because the "To" as expected in the array dimension is not a string but reserved word that assists in the operation of setting an array's dimension(s)
    'ReDim changing_dimension(1 & "To" & 3, 1 To 3, 1 To 3) 'does not work because the word "To" that is expected here in the array dimension is not a string but a reserved word that assists the operation of setting an array's dimension(s).
    'ReDim changing_dimension(1 To 3, 1 To 3, 1 To 3, 1 To 3)

    'attempt 1:
    For dimen = 1 To max_dim_bound
        If dimen < max_dim_bound Then
            dimension_string_attempt_1 = dimension_string_attempt_1 & upper_element_boundary & ","
            MsgBox (dimension_string_attempt_1)
        Else
            dimension_string_attempt_1 = dimension_string_attempt_1 & upper_element_boundary
        End If
    Next dimen
    MsgBox (dimension_string_attempt_1)
    ReDim changing_dimension(dimension_string_attempt_1) 'this does not change the nr of dimensions to 2, but just one dimension of "3" and "3" = "33" = 33 elements + the 0th element
    'changing_dimension(2, 1, 2, 1) = 4.5
    'MsgBox (changing_dimension(2, 1, 2, 1))
End Sub

*否则解决办法是:

  1. 导出模块的整个代码,并在维度的行替换数组的静态重新维度,使用准动态字符串dimension_string
  2. 删除当前模块
  3. 使用准动态字符串dimension_string 导入新模块作为代码中刷新的静态重新维度。

但是,这似乎令人费解,我很好奇是否有人知道更简单的解决方案。

请注意,这不是以下内容的重复:Dynamically Dimensioning A VBA Array? 尽管这个问题似乎意味着我在这里提出的问题,但这个问题的意图似乎是改变 nr。维度中的元素,而不是 nr。尺寸。 (区别在this article by Microsoft中讨论。)


为了尝试应用 Uri Goren 的答案,我分析了每一行并查找了他们所做的事情,并在其背后评论了我的理解,以便我的理解可以得到改进或更正。因为我不仅难以运行代码,而且难以理解它是如何回答问题的。该尝试包括以下步骤:

  1. 右键代码文件夹->插入->类模块然后点击: Tools>Options>“marked:Require variable declaration”如图 here 00:59。
  2. 接下来我将类模块重命名为

  3. 接下来我在类模块 FlexibleArray 中编写了以下代码:

    Option Explicit
    Dim A As New FlexibleArray
    Private keys() As Integer
    Private vals() As String
    Private i As Integer
    
    Public Sub Init(ByVal n As Integer)
       ReDim keys(n) 'changes the starting element index of array keys to 0 and index of last element to n
       ReDim vals(n) 'changes the starting element index of array keys to 0 and index of last element to n
       For i = 1 To n
            keys(i) = i 'fills the array keys as with integers from 1 to n
       Next i
    End Sub
    
    Public Function GetByKey(ByVal key As Integer) As String
       GetByKey = vals(Application.Match(key, keys, False))
       ' Application.Match("what you want to find as variant", "where you can find it as variant", defines the combination of match type required and accompanying output)
        'Source: https://msdn.microsoft.com/en-us/vba/excel-vba/articles/worksheetfunction-match-method-excel
        ' If match_type is 1, MATCH finds the largest value that is less than or equal to lookup_value. Lookup_array must be placed in ascending order: ...-2, -1, 0, 1, 2, ..., A-Z, FALSE, TRUE.
        ' If match_type is 0, MATCH finds the first value that is exactly equal to lookup_value. Lookup_array can be in any order.
        ' If match_type is -1, MATCH finds the smallest value that is greater than or equal to lookup_value. Lookup_array must be placed in descending order: TRUE, FALSE, Z-A, ...2, 1, 0, -1, -2, ..., and so on.
    
        'so with False as 3rd optional argument "-1" it finds the smallest value greater than or equal to the lookup variant, meaning:
        'the lowest value of keys that equals or is greater than key is entered into vals,
        'with keys as an array of 1 to n, it will return key, if n >= key. (if keys is initialized right before getbykey is called and is not changed inbetween.
    
       'vals becomes the number inside a string. So vals becomes the number key if key >= n.
    
    End Function
    
    Public Sub SetByKey(ByVal key As Integer, ByVal val As String)
       vals(Application.Match(key, keys, False)) = val
       'here string array vals(element index: key) becomes string val if key >=n (meaning if the element exists)
    
    
    End Sub
    
    Public Sub RenameKey(ByVal oldName As Integer, ByVal newName As Integer)
       keys(Application.Match(oldName, keys, False)) = newName
        'here keys element oldname becomes new name if it exists in keys.
    End Sub
    
  4. 然后我创建了一个新模块 11 并将下面的代码复制到其中,包括进行修改以尝试使代码正常工作。

    Option Explicit
    Sub use_class_module()
    Dim A As New FlexibleArray 'this dimensions object A but it is not set yet
    A.Init (3) 'calls the public sub "Init" in class module FlexibleArray, and passes integer n = 3.
    'A.SetByKey(1, "a") 'this means that Objecgt A. in class FlexibleArray  function SetByKey sets the private string array vals(1) in class Flexible Array becomes "a"
    'A.SetByKey(2, "b") 'this means that Objecgt A. in class FlexibleArray function SetByKey sets the private string array vals(2) in class Flexible Array becomes "b"
    'A.SetByKey(3, "c") 'this means that Object A. in class FlexibleArray function SetByKey sets the private string array vals(3) in class Flexible Array becomes "c"
    'A.RenameKey(3,5) 'This means that object A in class FlexibleArray keys element 3 becomes 5 so keys(3) = 5
    
    ' Would print the char "c"
    
    'to try to use the functions:
    'A.SetByKey(1, "a") = 4
    'MsgBox (keys("a"))
    'test = A.SetByKey(1, "a") 'this means that Objecgt A. in class FlexibleArray  function SetByKey sets the private string array vals(1) in class Flexible Array becomes "a"
    'MsgBox (test)
    'test_rename = A.RenameKey(3, 5) 'This means that object A in class FlexibleArray keys element 3 becomes 5 so keys(3) = 5
    'MsgBox (test_rename)
    'Print A.GetByKey(5) 'Method not valid without suitable object
    
    
    'current problem:
    'the A.SetByKey expects a function or variable, even though it appears to be a function itself.
    
    End Sub
    

我目前期望此代码将 my_array(3 到 4,5 到 9..) 替换为存在于/作为类模块 FlexibleArray 中的数组,当它需要在模块中使用时调用。但任何澄清将不胜感激! :)

【问题讨论】:

  • x To y中,To不是一个字符串,而是一个保留字,辅助设置数组维度的操作.将其放在引号中就像尝试使用 cells(1, 1)."copy" ;这根本不正确。
  • 谢谢你的解释,我很难用语言表达。我对代码进行了修改,以纳入您对几次尝试均无效的原因的评论。
  • 您能否提供更多示例来说明您正在尝试做什么?您是否有一个维度为 myArr(4 To 5, 3 To 6) 的数组,您想将其重新维度为 myArr(4 To 6, 3 To 7) ......这是一个有代表性的例子吗?
  • 在我的答案中添加了一个示例,为您提供一个真正灵活的重新维度数组 - 但您必须从第一个索引访问子元素。它是一个数组数组。可能必须更改访问您重新调整的数组的代码,但它应该适合您???
  • 哇,这真是一个很有创意的方法!这不是我提出问题时所考虑的形式的确切传统解决方案,但它确实再次有效地实现了目标,并稍微重写了我访问具有不同维度的数组的方式。而这次没有max_dim_bound的先决知识。它提醒人们跳出框框思考。非常感谢您的坚持,我已采纳!

标签: vba multidimensional-array dynamic dimensions


【解决方案1】:

如果重新调整数组的目标仅限于非荒谬的级别数,那么一个简单的函数可能对您有用,比如 1 到 4 维?

您可以传递表示每个维度的下限和上限的字符串,并传回重新调整维度的数组

Public Function FlexibleArray(strDimensions As String) As Variant

    ' strDimensions = numeric dimensions of new array
    ' eg. "1,5,3,6,2,10" creates ARRAY(1 To 5, 3 To 6, 2 To 10)

    Dim arr()               As Variant
    Dim varDim              As Variant
    Dim intDim              As Integer

    varDim = Split(strDimensions, ",")
    intDim = (UBound(varDim) + 1) / 2

    Select Case intDim
        Case 1
            ReDim arr(varDim(0) To varDim(1))
        Case 2
            ReDim arr(varDim(0) To varDim(1), varDim(2) To varDim(3))
        Case 3
            ReDim arr(varDim(0) To varDim(1), varDim(2) To varDim(3), varDim(4) To varDim(5))
        Case 4
            ReDim arr(varDim(0) To varDim(1), varDim(2) To varDim(3), varDim(4) To varDim(5), varDim(6) To varDim(7))
    End Select

    ' Return re-dimensioned array
    FlexibleArray = arr
End Function

通过使用数组边界调用它来测试它

Public Sub redimarray()
    Dim NewArray() As Variant

    NewArray = FlexibleArray("1,2,3,8,2,9")
End Sub

在调试模式下应该返回一个看起来像这样的数组

编辑 - 添加了真正动态的变体数组示例

这是获得真正灵活的重新维度数组的方法示例,但我不确定它是否是您正在寻找的,因为第一个索引用于访问其他数组元素。

Public Function FlexArray(strDimensions As String) As Variant

    Dim arrTemp     As Variant
    Dim varTemp     As Variant

    Dim varDim      As Variant
    Dim intNumDim   As Integer

    Dim iDim        As Integer
    Dim iArr        As Integer

    varDim = Split(strDimensions, ",")
    intNumDim = (UBound(varDim) + 1) / 2

    ' Setup redimensioned source array
    ReDim arrTemp(intNumDim)

    iArr = 0
    For iDim = LBound(varDim) To UBound(varDim) Step 2

        ReDim varTemp(varDim(iDim) To varDim(iDim + 1))
        arrTemp(iArr) = varTemp
        iArr = iArr + 1
    Next iDim

    FlexArray = arrTemp
End Function

如果您在 Debug 中查看它,您会注意到现在可以从返回数组的第一个索引访问的重新维度子数组

【讨论】:

  • 谢谢@dbmitch!它确实有效地解决了我正在尝试做的事情,但在技术上并不能保证完全回答这个问题,以防在编写代码之前不知道max_dim_bound 有多大。我很欣赏您写的答案如此清晰和明确,我希望这将与我们得到的一样接近。所以我会等到周日晚上,如果没有解决方案不需要事先了解max_dim_bound/确实允许荒谬的尺寸,我会接受你的回答!感谢您的努力!
  • 感谢您的反馈。是的,您应该在函数中添加一个max-dim-bound 常量,如果intDim 超过该值,则返回适当的错误消息。
【解决方案2】:

听起来您正在滥用数组来做一些与大量内存复制无关的事情。

你要的是自己写Class(代码文件夹右键->插入->类模块),我们就叫FlexibleArray吧。

你的课程代码是这样的:

Private keys() as Integer
Private vals() as String
Private i as Integer

Public Sub Init(ByVal n as Integer)
   Redim keys(n)
   Redim vals(n)
   For i = 1 to n
        keys(i) = i
   Next i
End Sub

Public Function GetByKey(ByVal key As Integer) As String
   GetByKey = vals(Application.Match(key, keys, False))
End Function

Public Sub SetByKey(ByVal key As Integer, ByVal val As String)
   vals(Application.Match(key, keys, False)) = val
End Sub

Public Sub RenameKey(ByVal oldName As Integer, ByVal newName As Integer)
   keys(Application.Match(oldName, keys, False))=newName
End Sub

现在你可以重命名你想要的任何键:

Dim A as New FlexibleArray
A.Init(3)
A.SetByKey(1, "a")
A.SetByKey(2, "b")
A.SetByKey(3, "c")
A.RenameKey(3,5)
Print A.GetByKey(5)
' Would print the char "c"

将其扩展到整数范围(如您的示例)非常简单

【讨论】:

  • 非常感谢@Uri Goren!我发现实施您的答案非常具有挑战性,而且我更难以理解它如何应用于该问题。所以我很抱歉,但这对我来说不是直截了当的。为了试图理解你的答案,我调查了你的每一行代码,并以我目前的理解对其进行了评论。我将研究您的解决方案并使用类模块函数进行更多练习,以尝试使您的解决方案正常工作。
猜你喜欢
  • 2016-01-28
  • 1970-01-01
  • 1970-01-01
  • 2011-01-28
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
相关资源
最近更新 更多