filter で要素を絞り込む
filter で要素を絞り込む とは
filter 関数で条件に合う要素だけを残す方法を学びます。本レッスンでは、filter で要素を絞り込む の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
filter で要素を絞り込む
前のレッスンで学んだ map が「全要素を変換する」役割なら、filter は「条件に合う要素だけ残す」役割を担う高階関数です。リストから偶数だけ取り出す、5 文字以上の単語だけ残す、None でない要素だけ集める、といった作業に最適です。
mapとfilterは兄弟のような関係です。前者は要素の 形を変える、後者は要素を 選別する。両方を組み合わせるとデータ整形の幅がぐっと広がります。
文法
filter(関数, イテラブル) という形で書きます。渡す関数は True / False を返す述語 (predicate) 関数です。
Python
nums = [1, 2, 3, 4, 5, 6]
result = filter(lambda x: x % 2 == 0, nums)
print(list(result))x % 2 == 0 が True の要素 (偶数) だけが残ります。出力は [2, 4, 6] です。filter の戻り値も map と同じくイテレータなので、list() で囲んで使うのが基本です。
述語関数とは
filter に渡す関数は、必ず真偽値 (True / False) を返す必要があります。
Python
def is_positive(x):
return x > 0
result = list(filter(is_positive, [-2, -1, 0, 1, 2]))
print(result)結果は [1, 2] です。def で関数を作って渡しても、lambda でその場で書いても、どちらでも構いません。
for ループとの比較
for ループで同じことをすると次の通りです。
Python
result = []
for n in nums:
if n % 2 == 0:
result.append(n)3 行が filter なら 1 行で済みます。コードを読む側にとっても「条件で絞り込んでいる」という意図がすぐに伝わります。
「if と append が組み合わさったら filter かも」とアンテナを張っておくと、自然と高階関数を活用できるようになります。
filter vs リスト内包表記
リスト内包表記でも同じことができます。
Python
result = [x for x in nums if x % 2 == 0]Python ではこの書き方の方が一般的です。filter は 既存の述語関数を再利用したいとき や、関数型プログラミング寄りのスタイル を採用したいときに使うのが良い選択です。
Python
names = ["", "太郎", "", "花子", None]
result = list(filter(None, names))
print(result)filter(None, ...) という特殊な書き方も使えます。これは truthy (真と見なせる) 値だけを残す書き方です。空文字や None が排除され、結果は ["太郎", "花子"] になります。
動きを追ってみる
filter の処理を図にします。
要素ごとに「合格」「不合格」を判定して、合格したものだけ新しいリストに集めます。map と違って 要素の中身は変えない のがポイントです。
map と filter の組み合わせ
データ処理では map と filter を組み合わせて使うことが多いです。
Python
nums = [1, 2, 3, 4, 5]
even_squared = list(map(lambda x: x * x, filter(lambda x: x % 2 == 0, nums)))
print(even_squared)偶数だけ取り出して 2 乗 する処理です。filter で [2, 4] に絞り込み、map で [4, 16] に変換しています。
関数を
パイプラインのように繋いでデータを流す書き方は、関数型プログラミングの面白さの 1 つです。
よくある間違い
- 述語関数が
True/False以外を返してしまう filterの戻り値をリストとして使おうとしてlist()で囲み忘れるfilterを 2 回回そうとすると 2 回目は空 (イテレータが消費済み)
Python
f = filter(lambda x: x > 0, [-1, 1, 2])
print(list(f))
print(list(f))2 回目は [] になります。filter は使い捨てのイテレータです。何度も使うなら最初に list に変換しておきましょう。
やってみよう
それでは、整数のリスト arr を受け取って、偶数だけを残した新しいリストを返す関数 keep_even を filter と lambda で書いてみましょう。x % 2 == 0 が偶数判定の条件です。テストでは keep_even([1, 2, 3, 4]) のような形で呼び出されます。
よくある質問
Q. filter と find の違いは?
A. filter は条件を満たす要素を全部集めて配列で返し、find は最初に見つかった 1 件だけ返します。1 件だけ欲しいなら find の方が短絡評価で速く、空のとき undefined(Python の next は StopIteration)になる点に注意してください。
Q. filter の戻り値が空配列のときの扱いは?
A. JS の filter は常に配列を返すため、空でも .length で安全にチェックできます。Python の filter はイテレータを返すので list() で囲んでから使うか、内包表記 [x for x in items if cond] の方が分かりやすいことが多いです。
Q. filter と map をチェーンしても性能は大丈夫ですか?
A. 通常規模なら問題ありません。配列を 2 周することになるため、要素数が数百万を超える場合だけ reduce にまとめるか、JS なら array.reduce で 1 周にすることを検討してください。可読性は filter→map のチェーンが分かりやすいです。
次のレッスン
次は reduce で畳み込む で、filter 関数で条件に合う要素だけを残す方法を学びます を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- filter の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. filter とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- filter と lambda を組み合わせて使う
- x % 2 == 0 で偶数を判定する
- list で囲んで結果をリストとして返す
入出力例
test-cases.txt
keep_even([1,2,3,4]) → [2,4]
keep_even([1,3,5]) → []
keep_even([2,4,6]) → [2,4,6]
keep_even([]) → []
keep_even([0,-2,-3,-4]) → [0,-2,-4]