【入門編】VBAで「動的配列」を操る:RedimとPreserveでデータ量を柔軟に扱う – Excel VBA解析バイブル

スポンサーリンク

こんにちは!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の基本はバッチリですよ。あなたの開発がもっと楽しく、効率的になることを応援しています!

タイトルとURLをコピーしました