Excel-VBA - 在数据字段数组中插入新的第一列,无需循环或 API 调用

Posted

技术标签:

【中文标题】Excel-VBA - 在数据字段数组中插入新的第一列,无需循环或 API 调用【英文标题】:Excel-VBA - Insert new first column in datafield array without loops or API calls 【发布时间】:2019-01-12 06:57:12 【问题描述】:

简介

去年@PrzemyslawRemin 提出了问题how to add a counter column to an existing matrix in VBA没有额外的循环没有修改工作表

本例中的原始矩阵是一个(基于 1 的 2-dim)数据字段数组,由(源单元格仅包含其地址字符串;插入的行将用数字填充)产生

Dim matrix As Variant
matrix = Range("A1:C5").value

输入矩阵:------------ ▼ 期望结果:

+----+----+----+   +----+----+----+----+ 
| A1 | B1 | C1 |   |  1 | A1 | B1 | C1 |
+----+----+----+   +----+----+----+----+ 
| A2 | B2 | C2 |   |  2 | A2 | B2 | C2 | 
+----+----+----+   +----+----+----+----+
| A3 | B3 | C3 |   |  3 | A3 | B3 | C3 | 
+----+----+----+   +----+----+----+----+ 
| A4 | B4 | C4 |   |  4 | A4 | B4 | C4 | 
+----+----+----+   +----+----+----+----+ 
| A5 | B5 | C5 |   |  5 | A5 | B5 | C5 | 
+----+----+----+   +----+----+----+----+ 

当然,建议自己的想法是使用重新调整的newMatrix,就像 Dy.Lee 建议的那样,但这将包括两个 循环 来移动行和列:

Sub test()
Dim matrix As Variant, newMatrix()
Dim i As Long, n As Long, c As Long, j As Long
matrix = Range("A1:C5").Value
n = UBound(matrix, 1)
c = UBound(matrix, 2)
ReDim newMatrix(1 To n, 1 To c + 1)
For i = 1 To n
    newMatrix(i, 1) = i
    For j = 2 To c + 1
        newMatrix(i, j) = matrix(i, j - 1)
    Next j
Next i
Range("a1").Resize(n, c + 1) = newMatrix
End Sub

另一个避免不必要循环的解决方法是将数组写回从 B 列开始的临时工作表,并从那里再次收集数据,包括 A:D 列,但这意味着修改工作表.

Florent B. 单独 solved 通过极快的 API 调用 使用 MemCopy 解决问题,此后似乎没有其他方法。 - 因此,出于主要原因,如果这应该是 ultima ratio 或者是否可以找到另一种方法,这会引起一些兴趣。

► 修改后的问题(无重复!)

是否有可能在现有数据字段数组中插入新的第一个“列”

无需循环“行”和“列”来移动现有值, 无需修改工作表和 没有使用 VBA 的 API 调用?

与 Prezmyslaw 的 OP 不同,我没有使用庞大的数据集,因此可能会限制为大约 64k 行(参见最大转置限制)。

【问题讨论】:

【参考方案1】:

通过Application.Index 函数找到解决方案

我通过尝试 Application.Index 函数的一些不寻常变体找到了一个解决方案,我尝试将其恢复为一个全面的通用概述以展示丰富的应用范围。因此,欢迎任何有用的补充(c.f. @chrisneilsen 的评论)。

Application.Index 函数的一些特性

通常,索引函数会通过其行和列位置提供明确定义的项目,但也有一些不那么广为人知的特性:

    Worksheet.Index 函数类似,如果行号或列号参数设置为零 (0),您可以获得整个列或行项目。 - 另一种通过传递双零参数创建 2-dim 数组的常见方法可以在 How to initialize a 2-dim array in Excel VBA

    找到

    可以使用数组参数 - 此函数不仅允许通过给定数字指示已知索引,还允许数组参数提取“行”或“列”,因此可以通过Array(1,2,3) 指示一组想要的列,例如A:C 作为列数组参数。

    过滤效果 - 此外,我了解到可以仅将选择减少到某些列(行),例如通过Array(1,3) and even to change the internal order, e.g. Array(3,2,1)`。

    重组 - 然而,最令人惊讶的事实是可以重复列选择,例如通过Array(1,1,2,3) 甚至Array(0,1,2,3),其中0 项与第1 列相同。这可用于达到与列插入相同的效果。

提到的Index 函数的这个最后的重组能力是我方法的关键部分:

代码示例

Sub AddFirstIndexColumn()
  Dim v, i&, ws As Worksheet
  Set ws = ThisWorkbook.Worksheets("SourceSheet")  ' << change to source sheet name
' [1] get data
      v = ws.[A1:C5].Value2
' [2] define column array inserting first column (0 or 1) and preserving old values (1,2,3)
      v = Application.Index(v, _
          Application.Evaluate("row(1:" & UBound(v) & ")"), _
          Array(0, 1, 2, 3))   ' columns array where 0 reinserts the first column
' [3] add an current number in the first column
      For i = LBound(v) To UBound(v): v(i, 1) = i: Next i
 End Sub

如何测试结果

只需在上面的代码中插入以下内容:

' [4a] test result by debugging in immediate window
      For i = LBound(v) To UBound(v)
          Debug.Print "#" & i & ": " & Join(Application.Index(v, i, 0), ", ")
      Next i
' [4b] test result by writing back to target sheet
      Dim ws2 As Worksheet
      Set ws2 = ThisWorkbook.Worksheets("TargetSheet")  ' << change to target sheet name
      ws2.Range("A1").Resize(UBound(v), UBound(v, 2)).Offset(0, 0) = v

警告

找到的解决方案似乎限制在 65536 行(可能类似于数组转置限制),因此您无法将其用于更大的数据。

最近的一些Application.Index 示例

Copy from sheet1 columns A,B,C,G,F,R,S,T to sheet2 in columns A,B,C,D,E,F,G,H Multi criteria selection with VBA How to join returned values from named range separated by comma

【讨论】:

仅供参考,虽然使用 INDEX 的方法很有趣,但对于 this 应用程序,它比循环方法或 API 方法慢得多。循环运行的时间大约是一半,而 API 又是一半(在 10,000 行范围内进行了测试)。 FWIW,我知道您将 Q 定义为“没有额外的循环......”,但恕我直言,这是一个没有价值的任意约束。如果代码需要在 Mac 上运行,则排除 API 调用将是有效的。 感谢您的宝贵反馈。我完全同意您的观点,即当时间 获取更大的数据集至关重要时,循环 是无与伦比的。 - 我的意图在这里是出于好奇和为了艺术而丰富传统解决方案的集合。在通过实验深入了解INDEX特性 时,我发现了重组可能性的真正宝藏,我试图将其列为有用的通用 概述。因此,欢迎像您这样的任何有用的补充。 @chrisneilsen 关于API方法:我故意排除了我在原帖中特别提到的API调用,因为在我看来,它已经得到了一定的限制。 - 但是,我正在考虑提出一个新的、扩展的问题,关注 API 问题 :-) @chrisneilsen

以上是关于Excel-VBA - 在数据字段数组中插入新的第一列,无需循环或 API 调用的主要内容,如果未能解决你的问题,请参考以下文章

Excel-VBA提高: 数组/日期处理

雪花表中json数据的解析字段将多行插入到新的雪花表中

温故之 “插入排序”

如何将来自三个 Perl 数组的数据插入到单个 MySQL 表中?

插入排序java

在SQL SERVER 的表中,插入新的字段