Preserve は「配列の中身を守ったままサイズを変える」ためのキーワード
まず一言でいうと、
Preserve は「動的配列の中身を消さずに、サイズだけ変えたいときに使う魔法の言葉」です。
普通の ReDim は「配列を作り直す」命令なので、
サイズを変えると中身が全部消えてしまいます。
そこで登場するのが ReDim Preserve。
「今入っているデータはそのままにして、箱の数だけ増やしたい(または減らしたい)」
というときに使います。
ReDim と ReDim Preserve の違いを体感する
ReDim だけだと中身は全部リセットされる
まずは、あえて失敗するパターンから見てみます。
Sub SampleReDimOnly()
Dim a() As Long
ReDim a(1 To 3)
a(1) = 10
a(2) = 20
a(3) = 30
' ここで「やっぱり5個にしたい」と思って ReDim し直す
ReDim a(1 To 5)
MsgBox a(1) ' 10 ではなく 0(初期値)が表示される
End Sub
VB流れを言葉で追うと、こうなります。
最初の ReDim で、a(1)~a(3) の3個分の箱を作る。
そこに 10, 20, 30 を入れる。
二回目の ReDim で、「1~5 の箱を新しく作り直す」。
そのタイミングで、前の 10, 20, 30 は全部捨てられる。
だから、最後に a(1) を表示しても 0 になってしまいます。
ここでの大事なポイントは、
「ReDim は“作り直し”なので、前の中身は残らない」
という性質です。
ReDim Preserve なら中身を残したまま広げられる
次に、Preserve を付けたパターンを見てみます。
Sub SampleReDimPreserve()
Dim a() As Long
ReDim a(1 To 3)
a(1) = 10
a(2) = 20
a(3) = 30
' 中身を残したまま、サイズだけ 5 個に広げる
ReDim Preserve a(1 To 5)
a(4) = 40
a(5) = 50
MsgBox a(1) & ", " & a(2) & ", " & a(3) & ", " & a(4) & ", " & a(5)
End Sub
VBこの場合は、
a(1)~a(3) に入っていた 10, 20, 30 はそのまま残り、
a(4), a(5) が新しく追加されます。
メッセージボックスには「10, 20, 30, 40, 50」と表示されます。
ここでの本質は、
「ReDim Preserve は“今ある中身を保ったまま、配列のサイズを変更する”」
ということです。
「見つかった分だけ配列を増やしていく」典型パターン
Preserve が一番“気持ちよくハマる”のは、
「何件見つかるか分からないけど、見つかった分だけ配列に詰めていきたい」
という場面です。
例えば、「A列の中で『完了』と書かれている行番号だけを配列に集めたい」とします。
Sub SamplePreserveCollect()
Dim results() As Long ' 行番号を入れる配列
Dim r As Long
Dim count As Long
count = 0
For r = 1 To 100
If Range("A" & r).Value = "完了" Then
count = count + 1
If count = 1 Then
' 最初の1件目:ここで初めて配列を作る
ReDim results(1 To 1)
Else
' 2件目以降:中身を残したままサイズを1つ増やす
ReDim Preserve results(1 To count)
End If
results(count) = r ' 見つかった行番号を格納
End If
Next r
If count > 0 Then
MsgBox "完了は " & count & " 件見つかりました。最初の行は " & results(1) & " 行目です。"
Else
MsgBox "完了は見つかりませんでした。"
End If
End Sub
VBやっていることを丁寧に言葉にすると、こうです。
「完了」を見つけるたびに count を1増やす。
1件目のときは ReDim results(1 To 1) で1個分の配列を作る。
2件目以降は ReDim Preserve results(1 To count) で、
それまでの中身を残したまま、箱を1つ増やす。
新しく増えた位置(results(count))に行番号を入れる。
Preserve がないと、二回目以降の ReDim で、
それまでに見つけた行番号が全部消えてしまいます。
ここでのキモは、
「増やすたびに ReDim Preserve で“これまでの結果”を守る」
という発想です。
Preserve の制限:多次元配列では「最後の次元」しか変えられない
少しだけレベルを上げた話もしておきます。ReDim Preserve には、重要な制限があります。
それは、
「多次元配列のとき、サイズを変えられるのは“最後の次元だけ”」
というルールです。
例えば、二次元配列をこう宣言したとします。
Dim table() As Long
ReDim table(1 To 3, 1 To 2)
VBこのとき、
ReDim Preserve table(1 To 5, 1 To 2) ' これはエラー(1次元目を変えようとしている)
ReDim Preserve table(1 To 3, 1 To 4) ' これはOK(2次元目=最後の次元だけ変更)
VBという違いが出ます。
超初心者の段階では、
「Preserve は“最後の次元だけ”サイズ変更できる」
くらいを覚えておけば十分です。
一次元配列(a(1 To n) のような形)なら、
気にせず ReDim Preserve a(1 To 新しい数) と書いて問題ありません。
Preserve を使うときに意識してほしいこと
「本当に中身を残したいのか?」を毎回自分に聞く
ReDim を書くとき、
「ここで中身を捨ててもいいのか?」
「それとも、今までのデータを残したいのか?」
この問いを、毎回自分に投げてみてください。
中身を残したいなら ReDim Preserve。
中身をリセットして構わないなら、普通の ReDim。
この切り替えを意識できるようになると、
「なぜか配列の中身が消えている…」という事故が一気に減ります。
「配列を育てていく」という感覚
Preserve を使うときのイメージは、
「最初は小さな箱から始めて、
必要に応じて、今の中身を抱えたまま、箱を少しずつ大きくしていく」
という感じです。
特に、「条件に合うものだけを順に集めていく」処理では、
Preserve で配列を“育てていく”感覚がとても気持ちよくハマります。
まとめ:Preserve は「配列の記憶を守る」ためのキーワード
Preserve の本質は、
「動的配列のサイズを変えるときに、
それまでに詰め込んだデータを守る」
という一点です。
押さえておきたいポイントをギュッとまとめると、こうなります。
普通の ReDim は「配列を作り直す」ので中身は全部消える。
ReDim Preserve は「中身を残したままサイズを変更する」。
「見つかった分だけ配列を増やす」ような処理で特に威力を発揮する。
多次元配列では「最後の次元」しか Preserve で変えられない。
