こんにちは!Excel VBAの世界へようこそ。
マクロの記録を卒業し、「もっと複雑な処理を自動化したい!」と思い始めたあなたが次にぶつかる壁。それが「処理するデータの数が、実行するたびに変わる」という問題です。
昨日は10行だったデータが、今日は1000行になっている。そんな変化に柔軟に対応し、かつパソコンのメモリを無駄遣いしないスマートなマクロを書くために欠かせない強力な武器、それが「動的配列(どうてきはいれつ)」です。
今回は、動的配列を操るための2つの合言葉`ReDim`と`Preserve`について、メモリの裏側で起きていることまで優しく、かつ徹底的に解説します。ここをマスターすれば、あなたのVBAスキルは「初心者」から「実務で頼られる開発者」へと一気にステップアップしますよ!
—
1. 静的配列と動的配列の決定的な違い
配列とは、「1つの名前で複数のデータをまとめて管理できる引き出し」のようなものです。
配列には、大きく分けて「静的配列」と「動的配列」の2種類があります。
静的配列:サイズ固定の頑丈なロッカー
Dim Members(1 To 10) As String ‘ 10個固定の配列
最初に「10個分」と決めたら、後から11個目を入れることはできません。逆に、3個しか使わなくても10個分のメモリスペースを占有し続けます。
動的配列:必要に応じて伸縮する魔法の棚
Dim Members() As String ‘ この時点ではサイズは未定(中身は空っぽ)
宣言時には引き出しの数を決めず、プログラムの途中で「今回は5個分確保しよう」「やっぱり10個に増やそう」と自由に変更できます。
【静的配列のイメージ】
[ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ][ 7 ][ 8 ][ 9 ][10] (増減不可・固定)
【動的配列のイメージ】
[ ] (最初はゼロ)
↓ ReDim
[ 1 ][ 2 ][ 3 ] (必要な分だけ確保)
—
2. 動的配列の基本作法:`ReDim` と `Preserve`
動的配列を使うためのステップは非常にシンプルです。
ステップ1:括弧の中を空にして宣言する
まずは「これから動的配列を使いますよ」という宣言です。
Dim customerNames() As String
※この時点では、メモリ上には「配列の看板」が作られただけで、データを格納する実体はまだありません。
ステップ2:`ReDim` で必要なサイズを確保する
データの個数が判明した時点で、`ReDim`(再次元設定)を使ってサイズを決定します。
ReDim customerNames(1 To 5)
これで、5個のデータを格納するスペースが確保されました。
ステップ3:`Preserve` で中身を保ったまま拡張する
ここが一番のポイントです。
一度データを格納した後に「あ、もう1つ引き出しを増やしたい!」となったとき、単に `ReDim customerNames(1 To 6)` と書くと、それまでに格納されていたデータがすべて消去(初期化)されてしまいます。
これまでのデータを消さずにサイズだけを変えたいときに使うのが、`Preserve`(保存する)キーワードです。
‘ 既存のデータを保持したまま、サイズを6に拡張する
ReDim Preserve customerNames(1 To 6)
—
3. 実践コード:データの件数に合わせて動的に格納する
では、実際の開発現場でよくある「A列に入力されているデータのうち、特定の条件(例:金額が1,000円以上)に合うものだけを動的配列に抽出し、別シートに書き出す」という処理を書いてみましょう。
コピペしてそのまま動かせるように、丁寧なコメント付きでコードを用意しました。
Sub ExtractHighValueData()
Dim srcSheet As Worksheet
Dim destSheet As Worksheet
Set srcSheet = ThisWorkbook.Sheets(“Sheet1”) ‘ データ元のシート
Set destSheet = ThisWorkbook.Sheets(“Sheet2”) ‘ 出力先のシート
Dim lastRow As Long
lastRow = srcSheet.Cells(srcSheet.Rows.Count, “A”).End(xlUp).Row
‘ — 【ポイント1】動的配列の宣言(最初は空っぽ) —
Dim highValueItems() As String
Dim itemCount As Long
itemCount = 0 ‘ 抽出したデータの個数を数えるカウンター
Dim i As Long
For i = 2 To lastRow ‘ 2行目から最終行までループ
Dim price As Double
price = srcSheet.Cells(i, 2).Value ‘ B列の「金額」を取得
If price >= 1000 Then
‘ 条件に一致したら、カウンターを増やす
itemCount = itemCount + 1
‘ — 【ポイント2】データを保持したまま配列を拡張 —
‘ ※LBound(最小インデックス)は1に固定し、上限だけを拡張します
ReDim Preserve highValueItems(1 To itemCount)
‘ 配列の最後にデータを格納(A列の商品名)
highValueItems(itemCount) = srcSheet.Cells(i, 1).Value
End If
Next i
‘ — 【ポイント3】抽出したデータを一気に出力 —
‘ 配列が一度も拡張されていない(該当データがゼロ)場合の処理
If itemCount = 0 Then
MsgBox “該当するデータはありませんでした。”, vbInformation
Exit Sub
End If
‘ 出力先をクリアして、配列の中身を縦方向に一括書き出し
destSheet.Cells.Clear
destSheet.Range(“A1”).Value = “抽出された商品名”
‘ WorksheetFunction.Transpose(転置)を使って、1次元配列を縦方向のセルに一気に流し込みます
destSheet.Range(“A2″).Resize(itemCount, 1).Value = Application.WorksheetFunction.Transpose(highValueItems)
MsgBox itemCount & ” 件のデータを抽出しました!”, vbInformation
End Sub
—
4. 動的配列を扱う際の「3大トラップ」と回避策
動的配列は非常に便利ですが、VBAの仕様上、いくつかの罠が存在します。エラーで頭を抱える前に、対策を知っておきましょう。
トラップ①:初期化前の配列にアクセスして「インデックスが範囲外です」
動的配列を宣言した直後(`ReDim` を行う前)に、配列のサイズを調べようとして `UBound(myArray)` などを実行すると、「実行時エラー 9: インデックスが範囲外です」 が発生します。
- 対策: 配列にデータが入っているかどうか、または最初に `ReDim` が実行されたかどうかをフラグ用の変数(上記のコードでの `itemCount` など)で管理するか、エラーハンドリングを行いましょう。
トラップ②:多次元配列で「最後の次元」以外を変更しようとする
動的配列は2次元(行と列のような構造)にすることも可能です。しかし、`Preserve` を使ってサイズを変更できるのは、多次元配列の「最後の次元(一番右側の次元)」だけという強力な制約があります。
Dim myMatrix() As Variant
ReDim myMatrix(1 To 5, 1 To 2)
‘ これはエラーになります(最初の次元を5から6に変更しようとしたため)
ReDim Preserve myMatrix(1 To 6, 1 To 2)
‘ これはOK(最後の次元を2から3に変更するのは可能)
ReDim Preserve myMatrix(1 To 5, 1 To 3)
- 対策: 行と列を逆(縦横を反転)にして配列にデータを格納していき、最後にセルへ書き出す際に `Application.WorksheetFunction.Transpose` で行列をひっくり返す、というテクニックが現場ではよく使われます。
トラップ③:`ReDim Preserve` の多用による速度低下
実は、`ReDim Preserve` はメモリにとても大きな負荷をかけます。
VBAの裏側では、`Preserve` が呼ばれるたびに「現在の配列と同じサイズの新しいメモリ領域を別の場所に確保し、古いデータをすべてコピーして、元の領域を消去する」という大移動が行われています。
ループの中で数万回も `ReDim Preserve` を繰り返すと、マクロの動作が著しく遅くなります。
【ReDim Preserve の裏側(メモリの再確保)】
[A][B][C] (手狭になったので引っ越します)
↓
[A][B][C][ ] (新しい広い土地に全員で引っ越し、空きを1つ作る)
- 対策: あらかじめ想定される最大サイズ(シートの最終行数など)で大きめに `ReDim` しておき、処理が終わった最後に実際のデータ件数に合わせて一回だけ `ReDim Preserve` で縮小する、というアプローチが極めてスマートです。
—
5. 先輩からのワンポイント・アドバイス
「データの増減に柔軟に対応したい」という目的を果たすための道具は、実は動的配列だけではありません。
もしあなたが「要素の追加や削除を、もっとシンプルに、インデックスの管理を気にせずに行いたい」と思うなら、VBAの `Collection` オブジェクト や `Dictionary`(連想配列) の使用も検討してみてください。
- 動的配列が向いているケース:
- 最終的にセルに「一括書き出し(貼り付け)」を行いたいとき(セルへの一括書き出しは配列が最速です)。
- メモリの消費量を極限まで抑えたいとき。
- Collection / Dictionary が向いているケース:
- データの追加(`Add`)や削除(`Remove`)が頻繁に発生するとき。
- キー(名前など)を使って、データに一瞬でアクセスしたいとき。
それぞれの道具の「得意分野」を理解して使い分けられるようになると、あなたの書くコードは一気にプロの品質へと昇華します。
—
まとめ
今回の内容を簡単におさらいしましょう。
1. 動的配列は、サイズを決めずに宣言し、後から大きさを変えられる便利な配列。
2. サイズの決定には `ReDim` を使い、中身を維持したまま拡張するには `ReDim Preserve` を使う。
3. `Preserve` は最後の次元しか変更できない点と、ループ内での多用による速度低下に注意する。
最初は少し難しく感じるかもしれませんが、実際に手を動かして、ローカルウィンドウで配列のサイズが変わる様子を観察してみると、「なるほど、こういうことか!」とパズルが解けるように理解できるはずです。
ここをクリアすれば、Excel VBAの基本はバッチリですよ。あなたの開発がもっと楽しく、効率的になることを応援しています!
