【问题标题】:ReDim Preserve with multidimensional array in Excel VBA在 Excel VBA 中使用多维数组进行 ReDim Preserve
【发布时间】:2014-09-25 12:57:16
【问题描述】:

我可以让它工作,但不确定这是正确还是最有效的方法。

详细信息:循环遍历 151 行,然后根据列 C 中的条件,仅将这些行中的列 AB 分配给二维数组。根据条件,数组中只需要 151 行中的 114 行。

我知道使用 ReDim Preserve,您只能调整最后一个数组维度的大小,而根本无法更改维度的数量。所以我使用LRow变量将数组中的行调整为总共151行,但我只需要在数组中的实际行在变量ValidRow中,所以看起来(151-114)= 37个多余的行由于 ReDim Preserve 行而在数组中。我想让数组尽可能大,它是 114 行而不是 151 行,但不确定这是否可行,请参阅下面的代码,非常感谢任何帮助,因为我是数组新手,并且已经花费了两个中最好的部分天看着这个。注意:列是常量,它们没有问题,但行会有所不同。

Sub FillArray2()

Dim Data() As Variant
Dim ValidRow, r, LRow As Integer

Sheets("Contract_BR_CONMaster").Select
LRow = Range("A1").End(xlDown).Row '151 total rows

Erase Data()

For r = 2 To LRow
 If Cells(r, 3).Value <> "Bridge From" And Cells(r, 3).Value <> "Bridge To" Then
  ValidRow = ValidRow + 1
  ReDim Preserve Data(1 To LRow, 1 To 2)
  Data(ValidRow, 1) = Range("A" & r).Value 'fills the array with col A
  Data(ValidRow, 2) = Range("B" & r).Value 'fills the array with col B
 End If

Next r

ActiveWorkbook.Worksheets("Test").Range("A2:B" & ValidRow + 1) = Data() 'assign after     loop has run through all data and assessed it

End Sub

【问题讨论】:

  • 您能否加大数组的大小,用您需要的内容填充它,然后只使用一次 ReDim Preserve 以将其缩小到所需的大小?
  • 感谢您的回复。如果我用常量在暗淡级别调整数组大小,然后在下面放入一个 ReDim,我会收到“数组已经标注尺寸”的错误。其次,在使用 ValidRow 变量完成 For/Next 循环之前,我不知道数组需要多少行。但我想我刚刚找到了一种使用转置的方法来回答我的问题。这似乎有效。

标签: arrays excel vba multidimensional-array


【解决方案1】:

我似乎已经通过使用行和列交换的转置来实现这一点,并且仍然使用 ReDim Preserve,然后在分配给范围时在最后转置。这样,数组的大小正是它需要的大小,没有空白单元格。

Sub FillArray3() 'Option 3 works using transposition where row and cols are swapped then swapped back at the end upon assignment to the range with no blank cells as array is sized incrementally via the For/Next loop

Dim Data() As Variant
Dim ValidRow, r, LRow As Integer

Sheets("Contract_BR_CONMaster").Select
LRow = Range("A1").End(xlDown).Row '151 total rows

Erase Data()

For r = 2 To LRow
 If Cells(r, 3).Value <> "Bridge From" And Cells(r, 3).Value <> "Bridge To" Then
  ValidRow = ValidRow + 1
  ReDim Preserve Data(1 To 2, 1 To ValidRow) 'can change the size of only the last dimension if you use Preserve so swapped rows and cols around
  Data(1, ValidRow) = Range("A" & r).Value 'fills the array with col A
  Data(2, ValidRow) = Range("B" & r).Value 'fills the array with col B
 End If

Next r

ActiveWorkbook.Worksheets("Test").Range("A2:B" & ValidRow + 1) = Application.Transpose(Data) 'swap rows and cols back

End Sub

【讨论】:

    【解决方案2】:

    另请注意,REDIM 的内部 VBA 实现不保证在缩小存储空间时释放存储空间。在这种实现中,一个常见的选择是在大小下降到小于输入大小的一半之前不物理地减少存储。

    您是否考虑过创建一个类型安全的集合类来存储这些信息而不是数组?在它的最基本形式中(对于 Integer 的存储类型),它看起来是一个类模块,如下所示:

    Option Explicit
    
    Private mData As Collection
    
    Public Sub Add(Key As String, Data As Integer)
        mData.Add Key, Data
    End Sub
    
    Public Property Get Count() As Integer
        Count = mData.Count
    End Property
    
    Public Function Item(Index As Variant) As Integer
        Item = mData.Item(Index)
    End Function
    
    Public Sub Remove(Item As Integer)
        mData.Remove Item
    End Sub
    
    
    Private Sub Class_Initialize()
        Set mData = New Collection
    End Sub
    

    此实现的一个特殊优势是大小逻辑完全从客户端代码中删除,这是应该的。

    请注意,这种模式存储的数据类型可以是 VBA 支持的任何类型,包括数组或其他类。

    【讨论】:

    • 出于好奇,您是否有任何 Redim 不会释放内存的示例?我刚刚尝试了数千次迭代的 redim,并且仅使用 2d 变体数组,内存似乎就线性减少了。
    • @ooo:不,我没有这方面的具体例子。然而,实现细节没有记录(据我所知),因此不能对可以依赖的行为做出假设,特别是在 EXCEL 版本之间。随着时间的推移,我希望 VBA 能够在 DOT NET 中重新实现,VBA 数组被实现为 DOT NET List 对象,其中大小将按照我在答案中的建议进行。
    • 感谢以上。从未在类模块中使用过类型安全,但我会带走它并尝试了解它是如何工作的,非常感谢。
    【解决方案3】:

    另外两种方法。 FillArray4 - 创建的初始数组太大,但代码的第二部分使用双循环将其移动到新数组,该循环将数组创建为所需的确切大小。

    Sub FillArray4()
    
    Dim Data() As Variant, Data2() As Variant
    Dim ValidRow As Integer, r As Integer, lRow As Integer
    
    Sheets("Contract_BR_CONMaster").Select
    lRow = Range("A1").End(xlDown).Row '151 total rows
    
    'Part I - array is bigger than it has to be
    Erase Data()
    
    For r = 2 To lRow
     If Cells(r, 3).Value <> "Bridge From" And Cells(r, 3).Value <> "Bridge To" Then
      ValidRow = ValidRow + 1 'this is the size the array needs to be 114 rows
      ReDim Preserve Data(1 To lRow, 1 To 2) 'but makes array to be 151 rows as based on lrow not ValidRow as cannot dynamically resize 1st dim of array when using preserve
      Data(ValidRow, 1) = Range("A" & r).Value 'fills the array with col A
      Data(ValidRow, 2) = Range("B" & r).Value 'fills the array with col B
     End If
    Next r
    
    'Part II
    'move data from Data() array that is too big to new array Data2() that is perfectly sized as it uses ValidRow instead of lrow
    Erase Data2()
    
    For i = LBound(Data, 1) To UBound(Data, 1) 'Rows
    For j = LBound(Data, 2) To UBound(Data, 2) 'Cols
     If Not IsEmpty(Data(i, j)) Then
      ReDim Preserve Data2(1 To ValidRow, 1 To 2)
      Data2(i, j) = Data(i, j) 'fills the new array with data from original array but only non blank dims; Data2(i,j) is not one particular row or col its an intersection in the array
      'as opposed to part one where you fill the initial array with data from cols A and B using seperate lines for each col
     End If
    
    Next
    Next
    ActiveWorkbook.Worksheets("Test").Range("A2:B" & ValidRow + 1) = Data2() 'assign data from new array to worksheet
    
    End Sub
    

    Sub FillArray5 - 简单得多,并且是我的首选,因为您只创建一个数组。初始循环确定数组需要的大小,然后第二个循环使用它来创建数组和存储数据。在这两种情况下只注意两个列。我在这种情况下遇到的问题是创建行不同的二维数组。这就是我去热带度假的时候了!

    Sub FillArray5()
    
    Dim Data() As Variant
    Dim ValidRow As Integer, r As Integer, lRow As Integer, DimCount As Integer,  RemSpaceInArr As Integer
    
    Sheets("Contract_BR_CONMaster").Select
    lRow = Range("A1").End(xlDown).Row
    
    Erase Data()
    
    For r = 2 To lRow
     If Cells(r, 3).Value <> "Bridge From" And Cells(r, 3).Value <> "Bridge To" Then
      ValidRow = ValidRow + 1 'this is the size the array needs to be 114 rows
     End If
    Next r
    
    DimCount = 0 'reset
     For r = 2 To lRow
      If Cells(r, 3).Value <> "Bridge From" And Cells(r, 3).Value <> "Bridge To" Then
       ReDim Preserve Data(1 To ValidRow, 1 To 2) 'makes array exact size 114 rows using ValidRow from first loop above
       DimCount = DimCount + 1 'need this otherwise ValidRow starts the dim at 114 but needs to start at 1 and increment to max of ValidRow
       Data(DimCount, 1) = Range("A" & r).Value 'fills the array with col A
       Data(DimCount, 2) = Range("B" & r).Value 'fills the array with col B
      End If
     Next r
     RemSpaceInArr = ValidRow - DimCount 'just a check it should be 0
    
    ActiveWorkbook.Worksheets("Test").Range("A2:B" & ValidRow + 1) = Data() 'assign data from array to worksheet
    
    End Sub
    

    【讨论】:

      猜你喜欢
      • 2021-05-07
      • 1970-01-01
      • 2016-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多