🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA の 2D 配列 — 二次元配列、グリッド、そして Range.Value

|

Excel VBA の 2D 配列 — 二次元配列、グリッド、そして Range.Value

TL;DR — VBA の 2D 配列は、二つ の添字を持つ 一つ の配列 — 行と列のグリッド、 g(row, column) — であって、配列の配列ではありません。Dim g(1 To 3, 1 To 4) は 3 行 × 4 列です。 Excel の作業で最も役立つ一つの事実はこれです。Range("A1:D3").Value を読むと 1 始まりの二次元 Variant 配列 が一手で手に入り、Range(...).Value = arr で配列を書き戻すと一手で書き込まれます — だからこそ配列処理は、セルを一つずつループするよりも劇的に速いのです。

Sub SheetToArrayAndBack()
    Dim data As Variant
    data = Range("A1:D100").Value          ' シートに一度アクセス -> 1 始まりの 2D 配列

    Dim r As Long
    For r = 1 To UBound(data, 1)           ' 行
        data(r, 4) = data(r, 2) * data(r, 3)   ' D 列 = B 列 * C 列、メモリ上で
    Next r

    Range("A1:D100").Value = data          ' 一度で書き戻す -> 瞬時
End Sub

2D 配列は、配列とワークシートが出会う場所です。シートは まさに グリッドであり、セルのブロックを扱う最速 の方法は、それを 2D 配列に取り込み、メモリ上で計算し、書き戻すことです。これは ReDim で配列にサイズを与える ことと UBound でその形を読む ことの上に、そのまま積み上がっています — グリッドは次元ごとに上限・下限を持つだけです。人がつまずく唯一の 点は、Range.Value の配列が 1 始まり であることと、ReDim Preserve では行を足せないことです。

この記事で学べること

  • 考え方の軸 — 二つの添字を持つグリッド g(row, column) であって、入れ子の配列ではない
  • 2D 配列を Dim g(1 To 3, 1 To 4)g(r, c) で宣言し、指定する
  • いちばん大事なこと — Range.Value1 始まり の 2D Variant 配列を一手で与える
  • 配列を範囲へ一度で書き戻す — そしてそれがなぜこれほど速いのか
  • 二つの境界ケース — 単一セルは配列では ない、単一列は (n, 1) になる
  • 行を足す — なぜ ReDim Preserve にはできないのか、そして代わりにどうするか
  • ジャグ配列(配列の配列)が本当の 2D 配列とどう違うか

考え方の軸:一つの配列、二つの添字

一次元配列は、番号の付いた箱の一列です。a(i)。二次元配列は グリッド です — 各箱を二つの数、行と列で 指定します。g(r, c)。肝心なのは、これは二通りに指定されるだけの 一つの 配列であって、要素それ自体がリ ストであるようなリストではない、ということです。

Dim g(1 To 3, 1 To 4) As Long   ' 3 行、4 列 = 12 要素
g(1, 1) = 10                     ' 1 行目、1 列目
g(3, 4) = 99                     ' 3 行目、4 列目

最初の添字は慣例的に行、二つめが列です — セル参照を読む順番と同じです。この順番を一貫させれば、2D 配列は セルの長方形のブロックへきれいに対応します。

グリッドを宣言してループする

二つの上限・下限を宣言し、それから入れ子の For でループします — 外側で行、内側で列 — 次元番号付きの UBound を使って。

Dim g(1 To 3, 1 To 4) As Long
Dim r As Long, c As Long
For r = LBound(g, 1) To UBound(g, 1)       ' 行
    For c = LBound(g, 2) To UBound(g, 2)   ' 列
        g(r, c) = r * 10 + c
    Next c
Next r

UBound(g, 1) は行数、UBound(g, 2) は列数です。各次元で LBound から UBound までループすれば、配列が 1 To nRange.Value が与える形)でも 0 始まりでも、同じコードが動きます。

いちばん大事なこと:Range.Value は 1 始まりの 2D 配列

これこそ、2D 配列を学ぶ価値を生む事実です。複数セルの範囲の .Value を読むと、一手で、上限・下限が 1 から始まる二次元 Variant 配列 が手に入ります — 範囲がシートのどこにあってもです。

Dim data As Variant
data = Range("C5:E7").Value      ' シート上のどこかにある 3 行 3 列のブロック
' data はいま data(1 To 3, 1 To 3) - つねに 1 始まり、行が先で列が後
Debug.Print data(1, 1)          ' C5 の値
Debug.Print data(3, 3)          ' E7 の値

二つのことに注意してください。第一に、配列は 0 始まりではなく 1 始まり です — なので data(1, 1) が左 上のセルであり、For i = 0 のループは最初の行を取りこぼして実行時エラー 9 にぶつかります。第二に、添字は ブロックに対する相対 であって、シートに対してではありません。data(1, 1) は、範囲が A1 でも C5 でも、その範囲の左上のセルです。受け取る変数は Variant として宣言してください(As Long() などではな く) — VBA が新しい 2D Variant を埋めてくれます。

一度で書き戻す

同じ技は逆向きにも効きます。正しい形の 2D 配列を範囲の .Value に代入すれば、Excel はブロック全体を一度 に書き込みます。

Range("C5:E7").Value = data     ' 9 セルすべてを一回の操作で書き込む

これが、配列のやり方が速い理由です。セルを一つずつ触ると、読み書きのたびに VBA と Excel の境界をまたぎま す — 大きな範囲なら何千回もの往復です。配列に読み込み、メモリ上で計算し、書き戻すなら、境界をまたぐのは合 計 二回 だけです。数百セルを超えるものなら、これは瞬時に感じられるマクロと、じりじり動くのを眺めるマ クロとの違いになります。書き込む範囲は配列の形(行 × 列)と一致していなければならないので、対象の範囲を UBound(arr, 1) 行 × UBound(arr, 2) 列にサイズ指定しましょう。

かみついてくる二つの境界ケース

単一セルは配列ではありません。 Range("A1").Value1 To 1, 1 To 1 の配列ではなく、素のスカラー値 を返します — なので配列を前提としたコードは、一セルの範囲で失敗します。範囲が単一セルの可能性があるなら、 添字を付ける前に IsArray() で判定しましょう。

単一の行や列でも、やはり 2D です。 Range("A1:A10").Valuedata(1 To 10, 1 To 1) — 10 行 1 列 — なので、data(i) ではなく data(i, 1) として指定します。単一の行でも同じで、data(1, j) です。シートの 「一次元」の切れ端では、二つめの添字を忘れがちです。

行を足す:ReDim Preserve にはできない

ReDim Preserve は最後の次元しかリサイズできない ので、データを保ったまま 2D 配列 の行数(最初の次元)を増やすことは できません。実用的な選択肢は三つです。

  • 一度でサイズを決める。 行数が事前に分かっているなら(最終行、レコード数)、ループの前にグリッドを最 終的なサイズへ ReDim し、Preserve はいっさい使いません。
  • 代わりに列を伸ばす。 拡張する次元が最後になるよう配列を列 × 行として宣言し、出力時に Application.Transpose で転置します。
  • 作り直す。 本当に増え方が分からないなら、Collection やジャグ構造に集めておき、最後に一度だけ 2D 配列を組み立てます。

シートの作業では、最初の選択肢がほぼいつも当てはまります。行数は、始める前に安上がりに知れるものだからで す。

ジャグ配列は別物

本当の 2D 配列は長方形です — どの行も同じ列数を持ちます。ジャグ 配列は、要素が それ自体 配列である ような一次元配列なので、各「行」が異なる長さを持てます。二つの添字ではなく、二組の角かっこで指定します。

Dim jagged(1 To 2) As Variant
jagged(1) = Array(1, 2, 3)      ' 3 要素の行
jagged(2) = Array(9, 8)         ' 2 要素の行
Debug.Print jagged(1)(3)        ' 3   <- (1, 3) ではなく、二組の角かっこ

ジャグ配列は不ぞろいのデータに便利ですが、つねに長方形である Range.Value とは 相互運用できません。 データが長方形なら — シートのブロックはつねにそうです — 本当の 2D 配列を使いましょう。

ExcelMaster の活用

セルのブロックを効率よく扱うのは一つのパターンです — Range.Value を 1 始まりの 2D 配列に読み込み、メモ リ上で計算し、一度で書き戻す — そして罠はどれも細部にあります。1 始まりの添字、単一セルのスカラー値、 Preserve では増やせない行です。

ExcelMaster には、やりたいことをその まま伝えられます — 「使用範囲の各行について、数量かける単価を E 列に入れて」 — すると、ブロックを 2D 配列 に読み込み、1 から UBound(arr, 1) までループし、結果を一回の代入で書き戻し、単一セルや単一列のケース も処理するので、マクロは正しく、かつ速くなります。ブックとコードはあなたの手元に残ります。

よくある質問

VBA で 2D 配列を宣言するには?

上限・下限を二つ、カンマで区切って与えます。Dim g(1 To 3, 1 To 4) As Long は 3 行 4 列のグリッドを作り ます。要素は二つの添字 g(row, column) で指定します。たとえば g(2, 3) です。動的な 2D 配列なら、 Dim g() で開いたまま宣言し、あとで ReDim g(1 To 3, 1 To 4) でサイズを与えます。

VBA で範囲を 2D 配列に読み込むには?

範囲の .ValueVariant に代入します。Dim data As Variant: data = Range("A1:D100").Value。VBA は一 手で 1 始まり の二次元配列 data(1 To rows, 1 To columns) を埋めます。セルは data(row, column) で指 定し、添字は範囲の左上に対する相対です。これが多数のセルを処理する最速の方法です。

Range.Value の配列が 0 始まりではなく 1 始まりなのはなぜですか?

Excel がそう返すからです — Range.Value の配列は、範囲がシートのどこにあっても、両方の次元で必ず添字 1 から始まります。LBound(data, 1) から UBound(data, 1) まで(あるいは単に 1 To UBound)ループすれば、 0 始まりのループで最初の行を飛ばしたり、末尾を越えて走ったりせずに済みます。

2D 配列を範囲へ書き戻すには?

範囲の .Value に代入します。Range("A1:D100").Value = data。範囲は配列と同じ形 — 同じ行数と列数 — でな ければならないので、対象を UBound(data, 1) 行、UBound(data, 2) 列にサイズ指定します。これはブロック全 体を一回の操作で書き込み、セルを一つずつよりもはるかに速いです。

VBA で 2D 配列に行を追加できますか?

ReDim Preserve ではできません — 最後の(列の)次元しかリサイズできないので、行数を増やすと実行時エラー 9 になります。ループの前に配列を最終的な行数へサイズ指定するか、列 × 行として宣言して転置するか、 Collection から組み立てて最後に一度だけ 2D 配列を作りましょう。

検証環境

動作確認: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-09-24.

関連ガイド: VBA ReDim · VBA UBound · VBA Array · VBA For Loop · VBA Range