動的配列とは、プログラムの実行中に要素数を増やしたり減らしたりできる配列です。この記事では、ReDimとReDim Preserveを使って、動的配列の要素数を変更したり要素を追加したりする方法を解説します。
配列の宣言や二次元配列、セル範囲との一括処理など、VBAの配列の基本は以下の記事で解説しています。
動的配列とは?静的配列との違いは?
配列は、複数の値を1つの変数名と番号(インデックス)でまとめて扱う仕組みです。
動的配列とは、配列の中でも格納できる要素の数が増減可能なもののことを指します。反対に、変数として宣言する際にあらかじめ要素数が決められていて、後から変更することができないものを静的配列と言います。
シートの行数や検索結果の件数のように、実行するまで要素数が分からないデータを扱うときは、動的配列を使います。
VBAで動的配列を使うには
変数の宣言や要素数の指定、値の代入など、VBAで動的配列を使う方法について解説していきます。
順に詳しく見ていきましょう。
動的配列を宣言する
まずは、動的配列を宣言する方法について紹介します。
基本的な構文は以下の通りとなります。
【基本構文】
Dim 配列名() As データ型
変数を宣言した段階では、配列内の要素数は未確定でメモリが確保されていない状態のため、配列に値を代入することはできません。宣言した配列を扱うには、後ほど紹介する方法で要素数を決める必要があります。
また、以下のように変数の宣言時に最大インデックス番号を指定することで、宣言と同時に要素数を確定することもできますが、その場合は静的配列となり数の増減は行えなくなるので注意が必要です。
【静的配列の場合の構文】
Dim 配列名(最大インデックス番号) As データ型
例外はありますが、基本的に配列のインデックス番号は0から順に数えていきます。そのため、例えば最大数に3を指定した場合、配列内に入る要素数は3つではなく合計4つとなります。
配列の要素数を指定する際は、必要な数から1を引いた数を指定するようにしましょう。
動的配列の要素数を決める
宣言した動的配列の要素数を決める方法はいくつかあります。要素数を決めて使える状態にすることを、動的配列の初期化と呼ぶこともあります。
1つは、ReDimステートメントを使用して要素数を指定する方法です。ReDimを使用する際の基本構文は以下の通りになります。
【基本構文】
ReDim 配列名(最大インデックス番号)
ReDimを実行することで、初めて動的配列の要素数が確定します。後ほど詳しく解説を行いますが、途中で要素数の増減を行いたい場合にもReDimステートメントを使用します。
また、Variant型の動的配列のみに限られますが、Array関数を使って要素数と初期値を同時に設定することも可能です。
【サンプルコード】
Dim arr() As Variant
arr = Array(1, 2, 3)
関数の戻り値がVariant型のため、変数の宣言時に他の型を指定するとエラーが発生してしまうので、注意が必要です。
変数を宣言する際は、Variant型を指定する場合に限り、括弧を付けずに変数名のみで宣言することもできます。
【サンプルコード】
Dim arr As Variant
arr = Array(1, 2, 3)
その他、格納したデータや要素数を全てクリアしたい場合は、Eraseステートメントを使用して配列をリセットすることができます。
【基本構文】
Erase 変数名
Eraseステートメントでリセットした場合、要素数に応じて確保したメモリが全て解放されるため、配列に再度アクセスする場合はReDimで要素数を決め直す必要があります。
動的配列の要素に値を代入する
続いて、動的配列の要素に値を代入する方法について解説します。
配列にデータを格納する際は、値を入れたい要素のインデックス番号を指定して代入します。
【サンプルコード】
Sub sampleArray()
Dim arr() As String
ReDim arr(2)
arr(0) = "サンプル1"
arr(1) = "サンプル2"
arr(2) = "サンプル3"
MsgBox "インデックス0番:" & arr(0) & vbCrLf & _
"インデックス1番:" & arr(1) & vbCrLf & _
"インデックス2番:" & arr(2)
End Sub
【実行結果】
動的配列に後から要素を追加する
冒頭でも解説しましたが、動的配列は要素数を後から変更することができます。
最初に指定した数以上の要素を追加したい場合は、ReDimで再度要素数を宣言します。
【サンプルコード】
Sub sampleRedim()
Dim arr() As String
Dim arrVal As Variant
ReDim arr(2)
Dim countBefore As Integer
For Each arrVal In arr
countBefore = countBefore + 1
Next arrVal
ReDim arr(3)
Dim countAfter As Integer
For Each arrVal In arr
countAfter = countAfter + 1
Next arrVal
MsgBox "追加前の要素数:" & countBefore & vbCrLf & _
"追加後の要素数:" & countAfter
End Sub
【実行結果】
ReDimステートメントを再度使用したことにより、要素数が変更されたのが分かると思います。
このように、ReDimを使って要素を追加したり、あるいは減らすことができますが、使用する上で気を付けなければいけない点があります。
ReDimステートメントは、要素数を変更する際に格納していたデータをクリアしてしまいます。そのため、元のデータを残しつつ要素を追加したい場合には、以下のようにPreserveを付ける必要があります。
【基本構文】
ReDim Preserve 配列名(最大インデックス番号)
Preserveがある場合と無い場合の違いについて、以下のサンプルコードで見てみましょう。
【サンプルコード】
Sub samplePreserve()
Dim arr() As Variant
Dim rdmVal As Variant, rdmPreVal As Variant
arr = Array(1, 2, 3)
ReDim arr(2)
rdmVal = arr(0)
arr = Array(1, 2, 3)
ReDim Preserve arr(2)
rdmPreVal = arr(0)
MsgBox "ReDim:" & rdmVal & vbCrLf & _
"ReDim Preserve:" & rdmPreVal
End Sub
【実行結果】
Preserveをつけたケースのみ、元の値が残されたままになっています。
ただし、変更後の要素数が元の数よりも少ない場合は、減った要素の数だけ後ろから順にデータが消えるので、注意が必要です。また、Preserveを付けたときは最初のインデックス番号(下限)を変更できません。
【応用】要素数に関わらず要素を追加する
必要な要素数が事前に分からないために、動的配列を採用するケースはよくあると思います。また、処理の内容に合わせて要素数を可変させることもあり得るでしょう。
例えば、配列の要素数に応じて何かの処理を行う場合、こうしたケースでは数値を事前に固定させることができないため、自動で要素数を取得できるように記述する必要があります。ですが、要素数そのものを取得するための機能は、残念ながらVBAには用意されていません。
要素数を自動で取得したい場合は、代わりにUBound関数を使用する方法があります。この関数は、配列の最大インデックス番号を取得することができる関数で、戻り値を元に要素数を計算することができます。
【UBoundの使用例】
要素数 = UBound(配列名) + 1
上記の式は、インデックス番号の開始数値が0の場合の計算式です。開始番号が0以外の配列も扱う場合は、最初の番号を返すLBound関数を使ってUBound(配列名) - LBound(配列名) + 1と計算します。
【サンプルコード】
Sub sampleUBound()
Dim arr() As Variant
Dim msg As String
arr = Array(1, 2, 3)
msg = "配列の要素数:" & UBound(arr) + 1 & vbCrLf
ReDim arr(UBound(arr) + 2)
msg = msg & "変更後の要素数:" & UBound(arr) + 1
MsgBox msg
End Sub
【実行結果】
このサンプルコードでは、主に2つの目的でUBound関数を使用しています。
1つ目は、配列の要素数を取得するためです。宣言した配列のインデックス番号は0から始まるため、先ほどの例と同じようにUBound関数の戻り値に1を足して、要素数を取得しています。
2つ目は、ReDimで要素数を追加する際に、指定するインデックス番号を自動で計算するためです。UBound関数を使用することで、変更前の要素数を元に数値を可変させることができるため、元の数を意識することなく要素を追加することができます。
このように、UBound関数と組み合わせて数値を計算することで、様々なケースに応用させることができます。
データの件数に合わせて1件ずつ追加する
実務でよくあるのが、「条件に合うデータだけを配列に集めたいが、何件になるかは実行するまで分からない」というケースです。この場合は、見つかるたびに件数を1つ増やし、ReDim Preserveで要素を1つずつ追加していきます。
次のコードは、セルA1〜A5のうち、空欄ではないセルの値だけを配列に集めます。
【サンプルコード】
Sub CollectNonEmpty()
Dim items() As String
Dim count As Long
Dim r As Long
For r = 1 To 5
If Cells(r, 1).Value <> "" Then
count = count + 1
ReDim Preserve items(1 To count) '要素を1つ増やす(値は残る)
items(count) = Cells(r, 1).Value
End If
Next r
If count = 0 Then
MsgBox "データがありません"
Else
MsgBox count & "件:" & Join(items, ", ")
End If
End Sub
【実行結果(A1〜A5に「りんご」「(空欄)」「みかん」「(空欄)」「バナナ」が入力されている場合)】
3件:りんご, みかん, バナナ
ポイントは、件数を数える変数countを使ってReDim Preserve items(1 To count)と書いていることです。一度もReDimしていない動的配列にもReDim Preserveは使えるので、最初の1件目から同じ書き方で追加できます。
また、1件も見つからなかった場合は配列に要素がないため、そのままitems(1)などにアクセスするとエラーになります。count = 0のときの処理を分けておきましょう。
件数が多いときは先にまとめて確保する
ReDim Preserveは、実行するたびに配列のサイズを変更する処理が行われます。件数が少なければ問題ありませんが、件数やデータ型によっては時間がかかることがあります。
Windows 11・Microsoft 365版Excel(64ビット)で試したところ、Variant型の配列に100万件を1件ずつReDim Preserveで追加すると約21.6秒かかりました。先に100万件分を確保してから代入した場合は約0.02秒でした(1回ずつ計測した参考値です。Long型やString型では差はもっと小さくなりました)。
件数の上限が分かっている場合は、先に最大件数で確保しておき、最後に1回だけReDim Preserveで切り詰めると、ReDim Preserveの実行は1回で済みます。
【サンプルコード】
Sub CollectNonEmptyFast()
Const MAX_ROWS As Long = 5
Dim items() As String
Dim count As Long
Dim r As Long
ReDim items(1 To MAX_ROWS) '最大件数で先に確保する
For r = 1 To MAX_ROWS
If Cells(r, 1).Value <> "" Then
count = count + 1
items(count) = Cells(r, 1).Value
End If
Next r
If count = 0 Then
MsgBox "データがありません"
Else
ReDim Preserve items(1 To count) '使った件数に切り詰める(1回だけ)
MsgBox count & "件:" & Join(items, ", ")
End If
End Sub
実行結果は1つ前のコードと同じ「3件:りんご, みかん, バナナ」です。
二次元配列で要素を追加するときの注意点
二次元配列でReDim Preserveを使う場合、変更できるのは最後の次元のサイズだけです。配列名(行, 列)の形で使っている場合、列は増やせますが、行を増やそうとすると「インデックスが有効範囲にありません」のエラーになります。
【サンプルコード】
Sub samplePreserve2D()
Dim data() As Variant
ReDim data(1 To 3, 1 To 2)
data(1, 1) = "佐藤"
ReDim Preserve data(1 To 3, 1 To 3) '最後の次元(列)は変更できる
'ReDim Preserve data(1 To 4, 1 To 3) '1つ目の次元(行)を変えるとエラーになる
MsgBox "列数:" & UBound(data, 2) & " data(1, 1):" & data(1, 1)
End Sub
【実行結果(メッセージボックスの表示)】
列数:3 data(1, 1):佐藤
表データの行を増やしたい場合は、行数の最大値で先に確保しておき、使った行数だけをシートに書き込む方法が簡単です。具体的なコードは、配列の基本を解説したVBA配列の記事の「動的配列で要素数を変更する」で紹介しています。
件数が分からないときはCollectionも選択肢
要素を順番に追加していくだけなら、VBA標準のCollection(コレクション)を使う方法もあります。CollectionはAddで要素を追加でき、ReDim Preserveで要素数を管理する必要がありません。
【サンプルコード】
Sub CollectWithCollection()
Dim items As New Collection
Dim r As Long
For r = 1 To 5
If Cells(r, 1).Value <> "" Then
items.Add Cells(r, 1).Value '要素数を気にせず追加できる
End If
Next r
MsgBox items.Count & "件:1件目は" & items(1)
End Sub
【実行結果(A1〜A5が1つ前と同じ場合)】
3件:1件目はりんご
Collectionの番号は1から始まります。ただし、Collectionはセル範囲にまとめて書き込んだり、Join関数で結合したりはできません。最終的にシートへ一括で書き込みたい場合は、配列を使うほうが便利です。
まとめ
今回は、動的配列について解説をしてきました。ポイントを振り返っておきましょう。
- 動的配列は
Dim 配列名() As データ型で宣言し、ReDimで要素数を決めてから使う - ReDimだけで要素数を変えると値が消える。値を残したまま追加するときはReDim Preserveを使う
- 件数が分からないデータは、件数を数えながら1件ずつ追加する。件数が多い場合は先にまとめて確保する
- 二次元配列のReDim Preserveで変更できるのは最後の次元だけ
通常の変数より処理が複雑な分、慣れるまでに少々時間がかかるかもしれませんが、マスターできるようにぜひ頑張ってみてくださいね!
関連記事
VBAの勉強方法は?
書籍やインターネットで学習する方法があります。昨今では、YouTubeなどの動画サイトやエンジニアのコミュニティサイトなども充実していて多くの情報が手に入ります。
そして、より効率的に知識・スキルを習得するには、知識をつけながら実際に手を動かしてみるなど、インプットとアウトプットを繰り返していくことが重要です。特に独学の場合は、有識者に質問ができたりフィードバックをもらえるような環境があると、理解度が深まるでしょう。
ただ、VBAに限らず、ITスキルを身につける際、どうしても課題にぶつかってしまうことはありますよね。特に独学だと、わからない部分をプロに質問できる機会を確保しにくく、モチベーションが続きにくいという側面があります。独学でモチベーションを維持する自信がない人にはプログラミングスクールという手もあります。費用は掛かりますが、その分スキルを身につけやすいです。しっかりと知識・スキルを習得して実践に活かしたいという人はプログラミングスクールがおすすめです。
プログラミングスクールならテックマニアがおすすめ!
ITスキル需要の高まりとともにプログラミングスクールも増えました。しかし、どのスクールに通うべきか迷ってしまう人もいるでしょう。そんな方にはテックマニアをおすすめします!これまで多くのITエンジニアを育成・輩出してきたテックマニアでもプログラミングスクールを開講しています。
<テックマニアの特徴>
・たしかな育成実績と親身な教育 ~セカンドキャリアを全力支援~
・講師が現役エンジニア ~“本当”の開発ノウハウを直に学べる~
・専属講師が学習を徹底サポート ~「わからない」を徹底解決~
・実務ベースでスキルを習得 ~実践的な凝縮カリキュラム~
このような特徴を持つテックマニアはITエンジニアのスタートラインとして最適です。
話を聞きたい・詳しく知りたいという方はこちらからお気軽にお問い合わせください。