VBAで2次元配列を使っていると、「格納する件数が事前に分からない」という場面によくぶつかります。以前、ReDim Preserveで2次元配列の行が増やせない原因と対処で解説した通り、ReDim Preserveで変更できるのは2次元配列の最後の次元(列)だけで、行数は変更できません。
この制約に対して、実務でよく使われる対処法は主に3つあります。
- 最大サイズで確保しておき、あとで正しいサイズに切り出す(スライス法)
- 先に件数を数えてから、ぴったりのサイズで確保する(2回ループ方式)
- ユーザー定義型(Type)の1次元配列を使う
このうち1つ目は前回の記事、3つ目はType配列で件数不明のデータをためる方法で紹介済みです。今回は2つ目の「先に件数を数える」方法を中心に、2次元配列の行数が決まらないときの3つの対処法を比較します。
パターン1|最大サイズで確保してから切り出す(スライス法)
対象データの行数を上限として2次元配列を確保しておき、格納が終わったあとに正しいサイズの配列へコピーし直す方法です。1回のループで格納できる代わりに、最後に配列のコピー処理が必要になります。該当件数が0件のときは切り出さず、件数を先に確認してから処理を分ける必要があります。詳しいコードは前回の記事をご覧ください。
パターン2|先に件数を数えてから、ぴったりのサイズで確保する(2回ループ方式)
1回目のループで条件に合う件数だけを数え、その件数ちょうどのサイズで2次元配列を確保してから、2回目のループで値を詰めていく方法です。データを2回読みに行く代わりに、切り出し(配列のコピー)処理が要りません。
' 1回目のループ:条件に合う件数を数える
Dim cnt As Long
cnt = 0
For i = 1 To srcCount
If 条件 Then
cnt = cnt + 1
End If
Next i
' ぴったりのサイズで確保
Dim result() As Variant
ReDim result(1 To cnt, 1 To 列数)
' 2回目のループ:条件に合う行だけ詰める
Dim r As Long
r = 0
For i = 1 To srcCount
If 条件 Then
r = r + 1
result(r, 1) = ...
result(r, 2) = ...
End If
Next i

スライス法との違いは次の2点です。
- 切り出し処理が不要:あらかじめぴったりのサイズで確保するため、パターン1のようなコピー処理を書く必要がありません。
- ループが2回になる:件数を数える分、データを2回走査します。
なお、該当する件数が0件のときは、パターン1と同じ注意が必要です。ReDim result(1 To 0, 1 To 列数)のように上限に0を指定すると、実行時エラー9「インデックスが有効範囲にありません。」になります。これは今回の方法に限った話ではなく、ReDimの一般的な仕様によるものです。実機で確認したところ、確保する行数が0件になる条件では、狙い通りこのエラーが発生しました。0件になり得る処理では、件数が0のときの分岐(配列を確保しない、処理を打ち切るなど)を用意しておく必要があります。

コードのSub名などは記事の説明用に付けたもので、再現用の最小コードです。
パターン3|ユーザー定義型(Type)の1次元配列を使う
1行分のデータをTypeで定義し、その1次元配列として扱う方法です。1次元配列なのでReDim Preserveで件数を増やせます。Variantへの直接代入ができないなど独自の制約があるため、詳しくはType配列の記事をご覧ください。
(参考)新しい配列にコピーしながら1行ずつ追記する方法
1行多いサイズで配列を宣言し直し、既存データをコピーしてから新しい行を足す、という方法も可能ではあります。ただし処理の流れが追いにくくなるうえ、件数が多いデータではコピーが繰り返し発生するため、実務で積極的に使う場面は多くありません。「こういう書き方もできる」程度の参考にとどめます。
どれを選ぶか
| 観点 | スライス法 | 件数先読み(2回ループ) | Type配列 |
|---|---|---|---|
| 切り出し(コピー)の要否 | 必要 | 不要 | 詰め替え時のみ必要 |
| データの走査回数 | 1回 | 2回 | 1回 |
| 0件のときの注意 | 必要 | 必要 | 不要(Preserveで対応可) |
| Variantへの直接代入 | 可能 | 可能 | 不可(制約あり) |
3つとも一長一短があり、「これが正解」というものはありません。処理を1回で済ませたいならスライス法、コードをシンプルに保ちたいなら件数先読み、件数が増減し続ける場面や1次元で完結させたいならType配列、というのが使い分けの目安になります。
まとめ
ReDim Preserveは2次元配列の行数を変更できないため、行数が決まらないデータを扱うには工夫が必要- 対処法は主に3つ:スライス法/件数先読み(2回ループ)/Type配列
- 件数先読み方式は切り出し処理が不要な代わりに、データを2回走査する
- どの方式でも、0件になり得る場合は事前の分岐が必須


コメント