🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA の Filter — 配列を 1 回で絞り込む、そして AutoFilter ではない理由

|

Excel VBA の Filter — 配列を 1 回で絞り込む、そして AutoFilter ではない理由

TL;DRFilter(sourceArray, match) は、部分文字列 match を含む要素だけを収めた 新しい 0 始まりの配列 を返します。これはシートではなく メモリ上の配列 に対する検索で あり、AutoFilter とは無関係です。噛みついてくるのは 3 つ。部分文字列 に一致すること (だから "AB""CAB" を残す)、既定で大文字小文字を区別する こと(無視するには vbTextCompare を渡す)、そして一致なしの結果はエラーではなく UBound = -1 の空配列 で あることです。任意の第 3 引数 Include はこれを反転させます。Filter(a, "x", False)"x" を含ま ない 要素をすべて残します。

Sub FilterDemo()
    Dim names As Variant, hits As Variant
    names = Array("Smith", "Jones", "Smithson", "Adams")

    hits = Filter(names, "Smith")          ' "Smith" を含む要素を残す
    Debug.Print hits(0)                     ' Smith      <- インデックス 0、常に 0 始まり
    Debug.Print hits(1)                     ' Smithson   <- 部分文字列一致、単語全体ではない
    Debug.Print UBound(hits)                ' 1          <- 2 件ヒット: インデックス 0 と 1

    hits = Filter(names, "smith")           ' 小文字 -> 既定では一致しない
    Debug.Print UBound(hits)                ' -1         <- エラーではなく空配列
End Sub

人をつまずかせる 2 つの引数を含めた完全な構文です。

Filter(sourceArray, match, [include], [compare])
'      1 次元配列    テキスト  True/False  vbTextCompare = 大文字小文字を無視

この記事で学べること

  • 考え方の軸 — Filter は配列を探し、残すべきものを返す
  • なぜ FilterAutoFilter では ない のか、そしてそれぞれをいつ使うのか
  • 部分文字列の罠:Filter は値全体ではなく単語の内側に一致する
  • 大文字小文字の区別と、vbTextCompare による解決
  • 空結果の罠(UBound = -1)とガードの仕方
  • フィルターを反転する Include:=False、そして Split と Join との往復

考え方の軸:Filter はシートではなく配列を探す

FilterInStr の配列版です。InStr が「この テキストは 1 つの 文字列の中にあるか、どこにあるか?」を問うのに対し、Filter は「これらの文字列の どれが このテキストを含むか?」を問い、含むものだけを収めた真新しい配列を返します。リストを渡せば、 より短いリストが返ってきます。

この捉え方が、いつ手を伸ばすべきかをそのまま教えてくれます。すでにメモリ上に 1 次元の文字列配列 がある — Split で切り出した断片、Dictionary のキー、 Dir が返すファイル名の一覧 — そして、何かに言及している部分集合が欲しい、という ときです。誰もが書くあのループを 1 行で置き換えます。

' 誰もが最初に書く長いやり方
Dim i As Long, n As Long
ReDim keep(UBound(names)) As String
For i = 0 To UBound(names)
    If InStr(1, names(i), "Smith") > 0 Then
        keep(n) = names(i): n = n + 1
    End If
Next i
ReDim Preserve keep(n - 1)

' 同じことを 1 回の呼び出しで
keep = Filter(names, "Smith")

消し去るべき混同:Filter は AutoFilter ではない

これは「なぜ Filter がシートで動かないのか」という質問の最大の発生源です。答えは、たまたま 1 つの語を 共有しているだけの、無関係な 2 つの道具を握っている、というものです。両者は互いの変種ではありません。

Filter() — 関数 AutoFilter — メソッド
対象 メモリ上の 1 次元配列 ワークシートの Range
返すもの 新しい小さな配列 何も返さない — UI で 行を隠す
セルに触れる? 決して触れない はい、目に見えるシートの状態
一致の種類 部分文字列、大文字小文字を区別 条件により 等しい / 含む / より大きい
こういうとき使う すでに配列を持っている ユーザーにフィルターした行を見せたい

データがセルにあって絞り込みたいなら、この関数ではなく AutoFilter を、多条件の コピーなら Advanced Filter を使います。Filter() が居場所を得るのは、 データを配列に取り込んだ あと です。よくある正しいパイプラインはこうです。列を読み、それを 1 次元配列に 平坦化し(Transpose の仕事)、それからシートにいっさい触れずにメモリ上で Filter する。

罠 1:Filter は値全体ではなく部分文字列に一致する

Filter は、一致テキストが要素の どこかに 現れる要素をすべて残します。これは「含む」検索にとっては 機能であり、「等しい」検索にとっては自分の足を撃つ罠です。

Dim codes As Variant, r As Variant
codes = Array("AB", "ABC", "CAB", "XY")
r = Filter(codes, "AB")
' r は "AB"、"ABC"、そして "CAB" を保持する — "AB" を含むすべての要素

"AB" に等しい要素だけが欲しかったなら、Filter は誤った道具です — 完全一致はできません。完全一致の 所属判定には、Dictionarydict.Exists(key))か Application.Match を 使います。「含む」が本当に意図していることであるときに Filter を使いましょう。

罠 2:Filter は既定で大文字小文字を区別する

そのままでは Filterバイナリ 比較を使います — "smith""Smith" に一致しません。これは 多くの人の頭の中の既定とは逆で、黙って空配列を返します。第 4 引数がこれを直します。

r = Filter(names, "smith")                        ' バイナリ -> "Smith" を取りこぼす
r = Filter(names, "smith", True, vbTextCompare)   ' テキスト -> "Smith"、"SMITH"、"smith" に一致

第 4 引数に届くには、第 3 引数(Include、ここでは True)を必ず与えなければならない点に注意して ください。代わりに、モジュール先頭の Option Compare Text は、そのモジュール内のすべての文字列比較を — Filter も含めて — 大文字小文字を無視にします。手軽ですが、モジュール全体の挙動を変えてしまうので、 効果を局所的にしたいときは呼び出しごとに vbTextCompare を渡しましょう。

罠 3:一致なしは空配列を返し、Include は反転させる

何にも一致しない Filter はエラーを出しません — 長さ 0 の有効な配列を返し、その UBound-1 です。For i = 0 To UBound(r) のループはこれを回っても無害に何もしませんが、r(0) に触ろうと すると「インデックスが有効範囲にありません」で落ちます。インデックスを取る前に UBound でガード しましょう。

r = Filter(names, "Zzz")
If UBound(r) < 0 Then
    MsgBox "No matches."
Else
    MsgBox (UBound(r) + 1) & " match(es) found."   ' 件数 = UBound + 1
End If

そして、ほとんど誰も覚えていない引数、Include。既定は True(一致を残す)です。False を渡すと Filter反転 します — 一致を含ま ない 要素をすべて残すのです。これが、リストから項目を 1 行で 取り除くやり方です。

' "temp" を含むパスをすべて捨てる
clean = Filter(paths, "temp", False)

意見:Filter は手元にある配列のためのもの — そして Split-Filter-Join の一行

Filter は優れた接着剤であり、貧弱なエンジンです。すでに 1 次元の文字列配列になっていて、「含む」が 本当の問いであるデータで輝きます — フォルダーの一覧を刈り込む、Split から得たトークンを絞る、ログ配列 からエラー行だけを残す。完全一致が欲しくなった瞬間(Dictionary を使う)、データがまだセルにある瞬間 (AutoFilter を使う)、あるいは要素が文字列でない瞬間(Filter は文字列に変換してテキストとして比較 するので、数値では静かに誤らせる)には、誤った答えになります。

最良の使い方は、3 つの関数のパイプラインの真ん中になることです。Split が解析し、Filter が残し、Join が 組み立て直す — 15 行のループを置き換える 1 つの式です。

' 1 つの CSV 行から、"2026" に言及するフィールドだけ残し、つなぎ直す
result = Join(Filter(Split(line, ","), "2026"), ", ")

この 1 行は、クラスター全体の縮図です。Split が文字列を配列にし、Filter が 部分集合を取り、Join がそれを 1 つに畳み込む — ループなし、インデックス計算なし、 off-by-one なし。

ExcelMaster の活用

フィルターすることが目的であることは、めったにありません。目的は「このエクスポートから期限切れの請求書を 抜き出し、テスト行を落として、顧客コードを寄こして」です。列を配列に読み込み、平坦化し、大文字小文字の 区別をガードし、空結果を処理し、残ったものをつなぎ直す頃には、配管が、本当に欲しかった 1 つの答えを 覆い隠してしまいます。ExcelMaster なら、その目的を普通の言葉で — 「期限切れでテスト用でないすべての請求書の顧客コードを一覧にして」 — と 言うだけです。そして、データを読み、本物の一致ロジックを適用し、まずファイルをバックアップする Python を 生成します。あなたは欲しい結果を説明するだけ。大文字小文字の区別も、空のケースも、エッジも、それが 処理します。

よくある質問

VBA の Filter 関数は何をしますか?

Filter(sourceArray, match) は、部分文字列 match を含む sourceArray の要素だけを収めた新しい配列を 返します。元は 1 次元配列でなければならず、結果は常に 0 始まりです。メモリ上の配列を検索してより小さな 配列を返すだけで、セルは一切変更しません。

VBA の Filter は AutoFilter と同じですか?

いいえ。Filter() はメモリ上の 1 次元配列の部分集合を取り、新しい配列を返す関数です。AutoFilter は 条件に基づいて可視シートの行を隠すワークシートのメソッドです。両者は名前を共有するだけで、ほかには何も ありません — データがセルにあるなら AutoFilter、すでに配列なら Filter() です。

テキストが明らかに一致するのに VBA の Filter が何も返さないのはなぜですか?

ほぼ確実に大文字小文字の区別です。Filter は既定でバイナリ比較を使うので、"smith""Smith" に 一致しません。第 4 引数に vbTextCompare を渡すか(Filter(a, "smith", True, vbTextCompare))、 モジュール先頭に Option Compare Text を置きましょう。

VBA の Filter で項目を除外するには?

第 3 引数 IncludeFalse を渡します。Filter(paths, "temp", False)"temp" を含ま ない 要素をすべて返します。Include を省略するか True にすると Filter は一致を残し、False にすると 非一致を残します。

VBA の Filter が何か一致を見つけたか確認するには?

結果の UBound を調べます。一致なしの Filter は、エラーではなく UBound-1 の空配列を返すので、 If UBound(result) < 0 Then は「何も一致しなかった」を意味します。配列は 0 始まりなので、一致の件数は UBound(result) + 1 です。

検証環境

検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-09-03.

関連ガイド: VBA Join · VBA Transpose · VBA Split · VBA AutoFilter · VBA Dictionary