はじめに
前回はFilterメソッドの前半となる Whereメソッドを実装しました。
VBA配列攻略3:Whereメソッドの実装
今回は後半となる PickRows メソッドを実装します。
実装コードを一部変更しました。2026.04.29
NG時はエラーを返さずヘッダーのみ返す思想としました。
- 未初期化の行番号配列が渡された場合に「ヘッダーのみ」を返却。
On Error Resume NextとErr.Clearを採用。- 使用例もシンプルにしました。
PickRowsの役割
PickRowsは、行番号配列を使って該当行を抽出し
「絞り込まれた新しい配列」を作るメソッドです。
Whereと組み合わせることで配列フィルタ処理を構成する基本的な部品になります。
入力:元データの配列 vData + 行番号配列 rows()
出力:抽出された配列
PickRowsは元データである vData を変更しません。
代わりに、抽出結果を 新しい配列として返します。
このように 元データを変更せず結果を返す設計を「非破壊(non-destructive)」と呼びます。
これにより元データを安全に再利用できるようになり、
デバッグ時にも不具合箇所を追いやすくなるメリットがあります。
シグネチャ
Public Function PickRows(ByRef vData As Variant, _
ByRef rows() As Long) As Variant
- vData:元となる2次元配列
- rows:抽出対象の行番号配列
- 戻り値:抽出された2次元配列
実装
Public Function PickRows(ByRef vData As Variant, _
ByRef rows() As Long) As Variant
'--- Setup --------------------------------
' 配列境界取得
Dim tr As TableBounds
tr = GetTableBounds(vData)
'--- ガード句:行番号リストの空チェック ---
On Error Resume Next
Dim rCount As Long
rCount = UBound(rows) - LBound(rows) + 1
' rowsが未初期化なら「ヘッダーのみ」を返して終了
If Err.Number <> 0 Then
rCount = 0
Err.Clear
End If
On Error GoTo 0
' 要素数が0件の場合の安全策
If rCount < 0 Then rCount = 0
'--- Allocate Result -----------------------
' 出力配列確保(rCount=0でもヘッダー分 tr.FirstRow は確保される)
Dim result() As Variant
ReDim result(tr.FirstRow To tr.FirstRow + rCount, _
tr.FirstCol To tr.LastCol)
'--- Header Copy ---------------------------
' ヘッダー行コピー(常に実行)
Dim c As Long
For c = tr.FirstCol To tr.LastCol
result(tr.FirstRow, c) = vData(tr.FirstRow, c)
Next
' 抽出対象がない場合はここで終了
If rCount = 0 Then
PickRows = result
Exit Function
End If
'--- Row Transfer --------------------------
' (以下、既存のデータコピー処理)
Dim r As Long
Dim srcRow As Long
Dim destRow As Long
destRow = tr.FirstRow + 1
For r = LBound(rows) To UBound(rows)
srcRow = rows(r)
For c = tr.FirstCol To tr.LastCol
result(destRow, c) = vData(srcRow, c)
Next c
destRow = destRow + 1
Next r
'--- Return --------------------------------
PickRows = result
End Function
実装のポイント
1. 行番号をそのまま利用する
PickRowsは、Whereが返した行番号配列を受け取り、
rows = [3, 7, 10]
その行番号をそのまま使って、該当行を新しい配列にコピーします。
vData(0, *) ' ヘッダーが行インデックス0の場合
vData(3, *)
vData(7, *)
vData(10, *)
2. TableBoundsで配列境界を統一
PickRowsでも、前回実装した TableBounds を利用して配列の境界を取得しています。
これにより次のメリットがあります。
- LBound / UBound を直接書かない
- 行・列の意味が明確になる
tr.FirstRow
tr.LastRow
tr.FirstCol
tr.LastCol
3. ヘッダー行はPickRows側で追加
Whereでは
For r = tr.FirstRow + 1 To tr.LastRow
としておりヘッダーを除いたデータ行のみを扱います。
PickRowsではヘッダーを追加する処理を入れます。
つまり、
- Whereが抽出対象を決める
- PickRowsはヘッダー付きでコピーする
という役割分担になっています。
使用例
Where の結果で処理を分岐します。
Dim rows() As Long
Dim cnt As Long
Dim result As Variant
cnt = Where(vData, "Price", opGreater, 100, rows)
' エラーだった場合(cnt=-1)、処理を分けます。
If cnt > -1 then
result = PickRows(vData, rows)
End If
このように、Whereで取得した行番号配列を PickRows に渡すことで
条件に一致した行だけを抽出できます。
この設計のメリット
この設計では、vDataの配列そのものではなく
「行番号」という軽量な情報を中心に処理を組み立てています。
1. フィルタと抽出を分離できる
例えば次のように、
rows1 = Where(条件1)
rows2 = Where(条件2)
rows = UniqueValue(rows1, rows2)
result = PickRows(vData, rows)
行番号配列を加工する処理を自由に挟むことができます。
この設計では「行番号配列」を中心に処理を組み立てるため、
様々な処理を組み合わせることができます。
- Where + PickRows(Filter)
- Where + DropRows(Delete)
- SortIndex + PickRows(Sort)
- 独自ロジック + PickRows または DropRows
2. データコピーを最小化できる
Whereではデータコピーを行わず、行番号だけ保持します。
そのため、複数条件を組み合わせてもデータコピーは最後のPickRowsだけになります。
次回
次回は、PickRowsと対になる DropRows(指定行を除外する処理)を実装します。これにより、データ抽出と削除の両方が行えるようになり、柔軟なデータ操作が可能になります。