はじめに
配列操作の Filter(フィルタ) 機能を実現するために、本連載では処理を次の3つの部品に分解して設計してきました。
- Where:条件に合う「行番号」を特定する
- PickRows:特定の行を「抽出」する
- DropRows:特定の行を「除外」する
前回の記事では、指定した行だけを取り出す PickRows を実装しました。今回はその対となる、指定した行を取り除く DropRows メソッドを実装します。
DropRowsの役割
DropRows は、引数で渡された行番号を 除外(削除) して、残りの行だけで新しい配列を再構築するメソッドです。
PickRows との違い
PickRowsとDropRowsの2つは相反するメソッドです。
例えば、5行のデータ(1行目はヘッダー)があり、3行目と4行目を除外したい場合:
1 ID, Name (Header)
2 01, Apple
3 02, Orange <-- 除外対象
4 03, Banana <-- 除外対象
5 04, Cherry
rows = [3,4]
1 ID, Name
2 01, Apple
5 04, Cherry
つまり
- PickRows = 指定行だけ抽出する
- DropRows = 指定行以外を抽出する
という対になる関係になります。
なぜ DropRows が必要なのか?
「条件に一致したものを取り出す」のが Where + PickRows ですが、実務では「削除条件を指定した方がシンプル」という場面も多くあります。
PickRows と DropRows を使い分けることで、抽出も削除も自由自在に行えるようになります。
DropRowsのシグネチャ
Public Function DropRows(ByRef vData As Variant, _
ByRef rows() As Long) As Variant
- vData:元となる2次元配列
- rows:削除対象となる行番号配列
- 戻り値:指定行が除外された新しい2次元配列
DropRowsの実装
「除外する行」を指定する代わりに、「除外されない行(残す行)」のリストを裏側で作って PickRows に丸投げするという戦略をとります。
Public Function DropRows(ByRef vData As Variant, _
ByRef rows() As Long) As Variant
'--- Setup --------------------------------
Dim tr As TableBounds
tr = GetTableBounds(vData)
'--- ガード句:除外リストが空の場合はそのまま返す ---
On Error Resume Next
Dim rowCount As Long
rowCount = UBound(rows) - LBound(rows) + 1
' rowsが未初期化なら「除外なし」として元データをそのまま返す
If Err.Number <> 0 Then
DropRows = vData
Err.Clear
On Error GoTo 0
Exit Function
End If
On Error GoTo 0
'--- 除外リストを高速判定用に変換 ----------------
' Dictionaryを使って除外対象をマーク(Existsで高速判定するため)
Dim dropMap As Object
Set dropMap = CreateObject("Scripting.Dictionary")
Dim i As Long
For i = LBound(rows) To UBound(rows)
dropMap(rows(i)) = True
Next i
' 削除不要ならそのまま配列を返す
If dropMap.count = 0 Then
DropRows = vData
Exit Function
End If
'--- 「残す行」の配列を作成 --------------------
' 元のデータ行を走査し、除外リストにない行番号だけを抽出
Dim keepRows() As Long
ReDim keepRows(1 To tr.rowCount) ' 最大サイズで確保
Dim r As Long
Dim cnt As Long
cnt = 0
' データ行を走査し、除外対象でない行番号だけをストック
For r = tr.FirstRow + 1 To tr.LastRow
If Not dropMap.Exists(r) Then
cnt = cnt + 1
keepRows(cnt) = r
End If
Next r
'--- 実行 ----------------------------------
' 「残す行」のリストを PickRows に渡して配列を再構築
If cnt = 0 Then
' 全て除外された場合:ヘッダーのみの配列を返す等の処理
' (※PickRowsの仕様に準拠)
Dim emptyRows() As Long
DropRows = PickRows(vData, emptyRows)
Else
' 有効な行だけをリサイズして PickRows へ渡す
ReDim Preserve keepRows(1 To cnt)
DropRows = PickRows(vData, keepRows)
End If
End Function
PickRows の仕様変更について(2026.04.29)
以前の記事で紹介した PickRows の実装コードを修正しました。内容はエラーハンドリングの追加です。記事をシンプルにするためエラーハンドリングはなしにしようと考えていましたが、ある程度そのまま使えるように最低限のものは入れるようにしたいと思います。
DropRows実装のポイント
-
PickRowsへの「丸投げ」による共通化
今回の最大の特徴は、自前で配列コピーを行わず、最後は必ず PickRows を呼んでいる点です。(DRY原則)- 削除対象が1つもないとき ⇒ vData をそのまま返す
- すべて削除されたとき ⇒ 空の配列を PickRows に渡し、ヘッダーのみ返してもらう
- 一部削除されたとき ⇒ 残す行リストを PickRows に渡す
このように「配列を新しく作る」という重い処理を一箇所(PickRows)に集約することで、コードの保守性が格段に向上します。
-
「除外」を「残す」に変換する
DropRows の核となるロジックは、以下の変換処理です。
・入力: 捨てたい行リスト (rows)
・変換: 元データ全体から rows を除いた「残したい行リスト」を作成
・出力: PickRows(vData, 残したい行リスト)
この変換を行う際、Dictionary オブジェクトの .Exists メソッドを利用することで、大量のデータがあっても「この行は除外対象か?」を高速に判定しています。 -
関心の分離
この設計により、各メソッドの役割が明確になります。
・PickRows:指定された行を物理的にコピーし、新しい配列を作る
・DropRows:どの行を残すべきかを判断し、指示書を作る
使用例
Where メソッドと組み合わせることで、「特定の条件に一致しないデータを除外する」といった操作が直感的に記述できます。
Dim rows() As Long
Dim cnt As Long
Dim result As Variant
' 在庫数が0の行番号を取得
cnt = Where(vData, "Stock", opEqual, 0, rows)
' 在庫0の行を除外して新しい配列を作成
' (※cnt=0 の場合は DropRows 内部でそのまま vData が返されます)
result = DropRows(vData, rows)
次回
これで、データの抽出PickRowsと除外DropRowsという、配列操作の両輪が揃いました。
次回は、これらの部品を組み合わせて、いよいよ本丸である Filter メソッド を完成させます。