Excel 根据VBA中的条件将二维数组复制到另一个数组
我想把数据从一张纸复制到另一张纸上 我将要复制的范围放入数组(LookupSource),因为在数组上工作比在单元格中循环更快 在填充二维数组(LookupSource)之后,我只想保留一些基于critieria的记录(A列=10000),因此我尝试从LookupSource将获取此条件的行复制到二维数组(DataToCopy),该数组将复制到目标工作表 我的问题是我无法做到这一点,因为我似乎无法对第二个数组(DataToCopy)的第一个维度(行)进行动态调整 你知道如何根据我的情况从LookupSource中填充数据到copy吗 我得到的错误“索引超出范围”位于以下行:Excel 根据VBA中的条件将二维数组复制到另一个数组,excel,vba,Excel,Vba,我想把数据从一张纸复制到另一张纸上 我将要复制的范围放入数组(LookupSource),因为在数组上工作比在单元格中循环更快 在填充二维数组(LookupSource)之后,我只想保留一些基于critieria的记录(A列=10000),因此我尝试从LookupSource将获取此条件的行复制到二维数组(DataToCopy),该数组将复制到目标工作表 我的问题是我无法做到这一点,因为我似乎无法对第二个数组(DataToCopy)的第一个维度(行)进行动态调整 你知道如何根据我的情况从Look
ReDim Preserve DataToCopy(1到j,1到6)
不是在第一次,而是在第二次输入For循环时,在下一次输入之后
我想这是因为J是可变的,我不允许改变数组的第一维
如何处理
我在做什么有更好的主意吗
举个例子,这里是我想复制的工作表的一小部分(我只复制了8行,但实际上有数千行)。我只想复制A列中有10000行的行
这是我的密码
Dim LookupSource as Variant
Dim DataToCopy() As Variant
Dim i As Long
Dim j As Long
With MySheet
'MyRange is a defined name that reprensent column A, B, C, D, E, F
LookupSource = .Range(.Range("MyRange")(1, 1), .Range("MyRange")(8, 6)).Value2
j = 1
For i = LBound(LookupSource) To UBound(LookupSource)
If LookupSource(i, 1) = 10073 Then
ReDim Preserve DataToCopy(1 to j, 1 to 6)
DataToCopy(j, 1) = LookupSource(i, 1)
DataToCopy(j, 2) = LookupSource(i, 2)
DataToCopy(j, 3) = LookupSource(i, 3)
DataToCopy(j, 4) = LookupSource(i, 4)
DataToCopy(j, 5) = LookupSource(i, 5)
DataToCopy(j, 6) = LookupSource(i, 6)
j = j + 1
End If
Next i
end with
范围查找函数
代码
Option Explicit
'START ****************************************************************** START'
' Purpose: Filters a range by a value in a column and returns the result '
' in an array ready to be copied to a worksheet. '
'******************************************************************************'
Function RangeLookup(LookUpValue As Variant, LookupRange As Range, _
Optional LookupColumn As Long = 1) As Variant
Dim LookUpArray As Variant ' LookUp Array
Dim DataToCopy As Variant ' DataToCopy (RangeLookup) Array
Dim countMatch As Long ' DataToCopy (RangeLookUp) Rows Counter
Dim r As Long, c As Long ' Row and Column Counters
' Check the arguments.
Select Case VarType(LookUpValue)
Case 2 To 8, 11, 17
Case Else: Exit Function
End Select
If LookupRange Is Nothing Then Exit Function
If LookupColumn < 1 Or LookupColumn > LookupRange.Columns.Count _
Then Exit Function
' Copy values of Lookup Range to Lookup Array.
LookUpArray = LookupRange
' Task: Count the number of values containing LookUp Value
' in LookUp Column of LookUp Array which will be
' the number of rows in DataToCopy Array.
' The number of columns in both arrays will be the same.
' Either:
' Count the number of values containing LookUp Value.
countMatch = Application.WorksheetFunction _
.CountIf(LookupRange.Columns(LookupColumn), LookUpValue)
' Although the previous looks more efficient, it should be tested.
' ' Or:
' ' Loop through rows of LookUpArray.
' For r = 1 To UBound(LookUpArray)
' ' Check if the value in current row in LookUp Column
' ' is equal to LookUp Value.
' If LookUpArray(r, LookupColumn) = LookUpValue Then
' ' Increase DataCopy Rows Counter.
' countMatch = countMatch + 1
' End If
' Next r
' Check if no match was found.
If countMatch = 0 Then Exit Function
' Task: Write the matching rows in LookUp Array to DataToCopy Array.
' Resize DataToCopy Array to DataToCopy Rows counted in the previous
' For Next loop and the number of columns in Lookup Array.
ReDim DataToCopy(1 To countMatch, 1 To UBound(LookUpArray, 2))
' Reset DataToCopy Rows Counter.
countMatch = 0
' Loop through rows of LookUp Array.
For r = 1 To UBound(LookUpArray)
' Check if the value in current row in LookUp Column
' is equal to LookUp Value.
If LookUpArray(r, LookupColumn) = LookUpValue Then
' Increase DataCopy Rows Counter.
countMatch = countMatch + 1
' Loop through columns of LookUp (DataToCopy) Array.
For c = 1 To UBound(LookUpArray, 2)
' Write the current value of LookUp Array to DataToCopy Array.
DataToCopy(countMatch, c) = LookUpArray(r, c)
Next c
End If
Next r
' Write values from DataToCopy Array to RangeLookup Array.
RangeLookup = DataToCopy
End Function
'END ********************************************************************** END'
如何克服多维数组中的
ReDim Preserve
限制
正如@ScottCraner所提到的,ReDim Preserve
只能更改给定(数据字段)数组的最后一个维度。
因此,尝试调整二维数组的第一个维度(““行”)的大小将失败
但是,应用相对未知的Application.Index()
(c.f.部分[2]
)过滤功能,您可以克服这一不便,并从较少循环的额外好处中获益
进一步阅读:见
帮助功能ValidRows
()
因评论而编辑(2020-04-22) 最常用
Application.Index()
的简短提示:
通常使用Application.Index()
函数
从二维数组中获取整个行或列数组,而无需循环。像这样访问基于1的二维数据字段数组需要 指示单个行或列的编号,并 将相邻参数列或行号分别设置为
0
(零),这可能导致
Dim horizontal, vertical, RowNumber As Long, ColumnNumber As Long
RowNumber = 17: ColumnNumber = 4
horizontal = Application.Index(data, RowNumber, 0)
vertical = Application.Index(data, 0, ColumnNumber)
(寻址单个数组元素将直接进行,但是通过data(i,j)
而不是理论上的应用程序。索引(数据,i,j)
)
如何使用Application.Index()
进行重组/筛选:
为了从Application.Index()
不仅需要将数组名(例如数据
)传递,还需要将行|列参数作为数组传递,例如
data = Application.Index(data, Application.Transpose(Array(15,8,10)), Array(1, 2, 3, 4, 5, 6))
请注意,rows参数通过换位变成“垂直”2维数组,其中数组(15,8,10)
甚至会更改现有的行顺序
(在上面的示例代码中,这是在ValidRows()
函数的最后一行代码中完成的)。
另一方面,columns参数数组(1,2,3,4,5,6)
保持“平坦”或“水平”且
允许按原样获取所有现有列值
因此,最终您将收到给定元素索引中的任何数据元素
(将它们视为图形中的坐标) 您只能调整数组的最后一个维度的大小。使用
Application.WorksheetFunction.CountIf()
在循环之前设置数组的大小,您将不需要ReDim Preserve
更好地过滤数据,复制可见单元格,然后将其粘贴到新的工作表上。更快的方法是什么?CountIf知道A列中有多少个数字为1000,或者过滤数据?那么如何只复制可见的部分呢?@ScottCraner我考虑过使用相同大小的源数组,当然我会在目标数组的末尾得到空白项,但是当我从一个数组复制到另一个数组时,它将不可见,因为它会复制单元格中的空值……你怎么看?那么,在这个想法和您提出的两个想法(CountIf/Filter)之间,哪一个更有效?非常感谢您的建议和努力,以这种方式循环还是使用CountIf来获得countMatch的数量更有效?在您的代码中,当您将数据重置为复制行计数器时,是否应该使用countMatch=1
而不是countMatch=0
?我们应该用LookUpArray(r,1)
替换LookUpArray(r,LookupColumn)
?@JustGreat:你让我有两个帐户,但countMatch保持为0,因为我在写之前正在计数。尝试在另一列中查找值时发现LookupColumn错误。谢谢。现在必须尝试此CountIf。再次感谢,将等待您关于CountIf的答案,但请告诉我您为什么添加了变量数据类型检查?@JustGreat:您可以安全地删除它,但我正在尝试使其防弹,以便您可以继续,例如:如果“DataToCopy=Empty,则退出Sub”。这是一种研究。如果这让您感到困扰,很抱歉。当然,另一方面,如果“DataToCopy”为空,您可能不知道原因。啊,利与弊。@JustGreat:我已经编辑了我的asnwer。这里有到的链接。我现在很忙。几小时后回来。祝你好运。这真的很有趣,非常感谢你提供的这些信息,但是我对Application.Index不是很了解。你能给我澄清一下吗?我所理解的是,它接受参数、数据、行和列,并且
Function ValidRows(arr, Condition) As Variant
'Purpose: a) check condition (e.g. values equalling 10000) and b) get valid row numbers c) in a 2-dim vertical array
ReDim tmp(1 To UBound(arr)) ' provide for 1-based 2-dim array
Dim i As Long, ii As Long
For i = 1 To UBound(arr) ' loop through 1st "column"
If arr(i, 1) = Condition Then ' a) check condition
ii = ii + 1: tmp(ii) = i ' b) collect valid row numbers
End If
Next i
ReDim Preserve tmp(1 To ii) ' resize tmp array (here the 1st dimension is also the last one:)
ValidRows = Application.Transpose(tmp) ' c) return transposed result as 2-dim array
End Function
Dim horizontal, vertical, RowNumber As Long, ColumnNumber As Long
RowNumber = 17: ColumnNumber = 4
horizontal = Application.Index(data, RowNumber, 0)
vertical = Application.Index(data, 0, ColumnNumber)
data = Application.Index(data, Application.Transpose(Array(15,8,10)), Array(1, 2, 3, 4, 5, 6))