乐于分享
好东西不私藏

Excel VBA里的“俄罗斯套娃”:当数组嵌套数组,你的CPU还好吗?

本文最后更新于2026-03-15,某些文章具有时效性,若有错误或已失效,请在下方留言或联系老夜

Excel VBA里的“俄罗斯套娃”:当数组嵌套数组,你的CPU还好吗?

各位表哥表姐们,大家好。

咱们今天不聊那些“如何把A列复制到B列”的幼儿园操作,也不扯什么Range(“A1”).Value = “Hello”这种连实习生都能盲打的入门货色。既然你点开了这篇文章,我猜你大概已经受够了ReDim Preserve像蜗牛一样的扩容速度,或者正在琢磨怎么在内存里搞个“多维宇宙”。

今天,咱们来聊点刺激的:VBA里的数组嵌套数组(Array of Arrays)。

这就好比你在俄罗斯套娃,打开一个娃娃,里面还有一个;再打开,还有一个……直到你发现最里面那个娃娃居然是一张写着“#N/A”的纸条。

一、为什么我们要搞“套娃”?

在VBA的世界里,标准的二维数组(Dim arr(1 to 10, 1 to 5))就像是一个规规矩矩的矩阵宿舍,每个人(元素)都有固定的床位(行和列)。这很好,很稳定,适合处理那种方方正正的Excel表格。

但是!生活(和数据)往往是不规则的。

想象一下,你要处理这样的数据:

第一行:张三,买了苹果、香蕉、橘子(3个水果)

第二行:李四,只买了榴莲(1个水果)

第三行:王五,买了葡萄、提子、车厘子、草莓、蓝莓(5个水果)

如果你用标准的二维数组,你得按最多的那一行(王五的5个)来定义列数。结果就是,张三和李四的后面全是空位,浪费内存不说,还得写一堆If IsEmpty()来判断有没有数据。

这时候,“数组嵌套数组”就闪亮登场了。

它的逻辑是:创建一个主数组,主数组里的每一个元素,不再是一个简单的字符串或数字,而是另一个数组。

MainArray(1) = [“苹果”, “香蕉”, “橘子”]

MainArray(2) = [“榴莲”]

MainArray(3) = [“葡萄”, “提子”, “车厘子”, “草莓”, “蓝莓”]

看到了吗?这就是VBA里的“动态二维数组”,或者说,Jagged Array(锯齿状数组)。每一行的长度都不一样,像锯齿一样参差不齐,却完美契合了你的数据。

二、实操环节:手把手教你“造娃”

别被理论吓跑了,代码才是硬道理。咱们直接上干货。

1. 声明阶段:给变体(Variant)一点尊重

首先,记住一条铁律:只有 Variant 类型的变量才能容纳数组。 你不能声明 Dim arr() As Integer 然后往里面塞数组,那样VBA会直接报错,表情比吃了没熟的柿子还苦涩。

Sub CreateJaggedArray()

    Dim mainArr() As Variant  ‘ 主数组必须是 Variant

    Dim i As Long

    ‘ 假设我们有3行数据

    ReDim mainArr(1 To 3)

    ‘ 开始套娃!

    ‘ 第1个元素:塞入一个包含3个元素的数组

    mainArr(1) = Array(“苹果”, “香蕉”, “橘子”)

    ‘ 第2个元素:塞入一个包含1个元素的数组

    mainArr(2) = Array(“榴莲”)

    ‘ 第3个元素:塞入一个包含5个元素的数组

    mainArr(3) = Array(“葡萄”, “提子”, “车厘子”, “草莓”, “蓝莓”)

    Debug.Print “套娃制作完成!”

End Sub

看,就是这么简单。Array() 函数返回的就是一个基于0的 Variant 数组,我们把它直接赋值给 mainArr 的某个元素。此时,mainArr(1) 本身就是一个数组。

2. 读取阶段:剥开套娃的艺术

存进去容易,取出来就得小心了。你不能直接用 mainArr(1, 2) 这种写法(那是标准二维数组的语法)。你需要两次寻址。

Sub ReadJaggedArray()

    Dim mainArr() As Variant

    Dim subArr() As Variant ‘ 用来接收内部的小数组

    Dim i As Long, j As Long

    Dim output As String

    ‘ 重新初始化数据(为了演示完整性)

    ReDim mainArr(1 To 3)

    mainArr(1) = Array(“苹果”, “香蕉”, “橘子”)

    mainArr(2) = Array(“榴莲”)

    mainArr(3) = Array(“葡萄”, “提子”, “车厘子”, “草莓”, “蓝莓”)

    ‘ 遍历主数组

    For i = LBound(mainArr) To UBound(mainArr)

        ‘ 【关键点】:先把内部的数组取出来,赋值给一个临时的Variant数组变量

        ‘ 这一步相当于把大娃娃打开,拿出里面的小娃娃

        subArr = mainArr(i)

        output = “第” & i & “位顾客买了:”

        ‘ 遍历内部的小数组

        ‘ 注意:Array()函数生成的数组下标默认是从0开始的!

        For j = LBound(subArr) To UBound(subArr)

            output = output & subArr(j) & “、”

        Next j

        ‘ 去掉最后一个顿号,强迫症福音

        If Len(output) > 0 Then output = Left(output, Len(output) – 1)

        Debug.Print output

    Next i

End Sub

运行结果(F8走起):

第1位顾客买了:苹果、香蕉、橘子

第2位顾客买了:榴莲

第3位顾客买了:葡萄、提子、车厘子、草莓、蓝莓

3. 进阶玩法:从Excel范围直接“套娃”

当然,没人会手动写 Array(“苹果”, “香蕉”),我们的数据都在Excel表格里。怎么把不规则的区域变成嵌套数组?

这里有个小技巧:利用 Application.Transpose 或者直接循环读取。但最优雅的方式,是结合 Range.Value 的特性。

注意:Range.Value 读出来的是二维数组。如果要转成“数组的数组”,我们需要稍微加工一下。

Sub ExcelToJaggedArray()

    Dim ws As Worksheet

    Dim rng As Range

    Dim mainArr() As Variant

    Dim i As Long, lastRow As Long

    Dim cellVal As Variant

    Set ws = ThisWorkbook.Sheets(“Sheet1”)

    ‘ 假设A列是名字,B列开始是不定长的水果列表(用逗号分隔,或者已经在不同单元格?)

    ‘ 场景假设:每行数据长度不一,我们模拟读取每一行的有效区域

    lastRow = ws.Cells(ws.Rows.Count, “A”).End(xlUp).Row

    ReDim mainArr(1 To lastRow)

    For i = 1 To lastRow

        ‘ 假设我们要读取第i行,从B列开始,直到遇到空单元格

        ‘ 这里演示一种动态构建内部数组的方法

        Dim tempCol As New Collection

        Dim colIndex As Long

        colIndex = 2 ‘ 从B列开始

        Do While Not IsEmpty(ws.Cells(i, colIndex))

            tempCol.Add ws.Cells(i, colIndex).Value

            colIndex = colIndex + 1

        Loop

        ‘ 将Collection转为Array (需要一个小技巧,因为Collection不能直接变Array)

        If tempCol.Count > 0 Then

            Dim tempArr() As Variant

            ReDim tempArr(0 To tempCol.Count – 1)

            Dim k As Long

            For k = 1 To tempCol.Count

                tempArr(k – 1) = tempCol(k)

            Next k

            mainArr(i) = tempArr ‘ 嵌套成功!

        Else

            mainArr(i) = Array() ‘ 空数组

        End If

    Next i

    MsgBox “已成功将不规则区域转换为嵌套数组,共 ” & lastRow & ” 行数据!”

End Sub

三、避坑指南:那些让你半夜哭醒的坑

作为过来人,我有义务提醒你几个“血泪教训”:

下标起始值的混乱:

你自己 ReDim 的数组,默认下标取决于你的 Option Base 设置(通常是0或1)。

但是!VBA内置的 Array() 函数返回的数组,永远是从0开始的,不管你怎么设置 Option Base 1。

后果:当你遍历内部数组时,如果不小心写了 For j = 1 To UBound(subArr),你会完美错过第一个元素(下标0),并且可能报“下标越界”或者逻辑错误。切记:处理 Array() 生成的子数组,请用 LBound 和 UBound,别偷懒写死数字!

类型检查的必要性:

因为主数组是 Variant,它理论上可以塞进任何东西:数字、字符串、日期,甚至另一个对象。

如果在复杂的宏里,你不确定某个元素是不是数组,先用 IsArray(mainArr(i)) 判断一下。否则,当你试图对一个数字执行 UBound 时,VBA会毫不留情地甩给你一个“类型不匹配”的错误。

性能的双刃剑:

嵌套数组在处理不规则数据时效率极高,因为它避免了大量的空值判断和内存浪费。

但是,频繁的“拆包”和“打包”(即反复访问 mainArr(i)(j))在超大规模循环中,比直接访问标准二维数组稍微慢那么一丢丢(真的只是一丢丢,人类感知不到,除非你在处理千万级数据)。

最佳实践:像上面代码演示的那样,先把子数组赋值给一个临时变量 subArr = mainArr(i),然后再循环 subArr。这样能减少VBA解释器的解析开销。

四、结语:让数据飞一会儿

朋友们,掌握“数组嵌套数组”,你就掌握了VBA处理非结构化数据的钥匙。它让你的代码不再是被Excel表格形状束缚的囚徒,而是能在内存中自由构建数据模型的架构师。

下次当你看到那些长短不一的数据行,别再苦哈哈地写 If Cells(i, j) <> “” Then 了。掏出你的 Variant,吹起你的“套娃”号角,让数据在数组的嵌套中翩翩起舞吧!

最后送大家一句话:

代码如人生,不必处处整齐划一。偶尔的“锯齿状”,才是真实的模样。

觉得有用?点个小赞赞,分享,转发给那个还在用 ReDim Preserve 死磕的同事。毕竟,独乐乐不如众乐乐,大家一起头发少一点,世界更美好。

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » Excel VBA里的“俄罗斯套娃”:当数组嵌套数组,你的CPU还好吗?

猜你喜欢

  • 暂无文章