Python | データ構造:filter

Python
スポンサーリンク

概要(filter は「条件を満たす要素だけ」を取り出す基本関数)

filter は、リストやタプルなどの各要素に「判定関数」を適用し、True と評価された要素だけを取り出すための組み込み関数です。戻り値は filter オブジェクト(イテレータ)で、必要に応じて list(…) や tuple(…) に変換して中身を使います。for と if を組み合わせるより短く、意図が明確に書けます。

def is_even(n): 
    return n % 2 == 0

nums = [1, 2, 3, 4, 5, 6]
evens = list(filter(is_even, nums))
print(evens)  # [2, 4, 6]
Python

基本の使い方と挙動(ここが重要)

書式と戻り値の性質

書式は filter(function, iterable) です。function は各要素を受け取り、True/False を返す判定関数です。戻り値は「遅延評価のイテレータ」で、一度消費すると再利用できません。結果を複数回使うなら、最初に list(…) へ変換します。

texts = ["apple", "", "banana", ""]
non_empty_iter = filter(lambda s: s != "", texts)  # イテレータ
print(list(non_empty_iter))  # ['apple', 'banana']
# print(list(non_empty_iter))  # 空(すでに消費済み)
Python

function=None の特殊動作(“真”な要素だけを通す)

function を省略(None を渡す)すると、「各要素の真偽値(bool(…))」でフィルタされます。0、空文字、空リスト、None、False などは除外され、非空・非ゼロだけが残ります。

values = [0, 1, "", "ok", [], [1], None, True]
print(list(filter(None, values)))  # [1, 'ok', [1], True]
Python

ひとつの iterable を最後まで評価する

filter は全要素に判定関数を適用します(全体の“短絡”はありません)。判定関数自体が短絡する場合(例:複合条件の or)を除き、最後まで走査して必要な要素を拾います。


よく使うパターン(数値・文字列・辞書に対するフィルタ)

数値リストから条件で抽出する

閾値、偶数・奇数、範囲判定などを短く書けます。

nums = [10, -5, 30, -2, 0]
non_negative = list(filter(lambda n: n >= 0, nums))
print(non_negative)  # [10, 30, 0]
Python

文字列の前処理と欠損除去

トリム後に非空だけ通すなど、現実的な前処理に向いています。

rows = ["  apple ", " ", "banana", ""]
clean = list(filter(None, map(lambda s: s.strip(), rows)))
print(clean)  # ['apple', 'banana']
Python

辞書の配列から条件を満たすレコードを抽出

フィールド値で判定し、必要なレコードだけ残します。

products = [
    {"name": "coffee", "price": 350, "qty": 2},
    {"name": "tea",    "price": 280, "qty": 0},
    {"name": "juice",  "price": 220, "qty": 3},
]
in_stock = list(filter(lambda p: p["qty"] > 0, products))
print(in_stock)  # coffee と juice
Python

filter と内包表記の使い分け(重要ポイントを深掘り)

読みやすさの指針

「条件でふるいにかける」だけなら、[x for x in iterable if 条件] のリスト内包表記は Python で広く使われ、読みやすいです。既存関数をそのまま適用して判定したい、または map と連携して“変換→判定”の流れを素直に書きたい場合は filter がすっきりします。

nums = [1, 2, 3, 4]
print([n for n in nums if n % 2 == 0])        # 内包表記
print(list(filter(lambda n: n % 2 == 0, nums)))  # filter
Python

変換とフィルタの連携(map → filter)

「まず整形してから基準で選ぶ」のように、前処理と選別を段階的に重ねると意図が明確になります。

rows = [" 10", "x", " 25 ", "7"]
to_int_safe = lambda s: int(s) if s.strip().isdigit() else None
converted = map(to_int_safe, rows)
valid = list(filter(None, converted))
print(valid)  # [10, 25, 7]
Python

つまずきやすい点と安全策(イテレータ・例外・性能)

イテレータは一度きり

filter の戻り値は使い切りです。複数回使うなら最初に list(…) に変換するか、必要なタイミングで新たに filter を作ります。

it = filter(lambda x: x > 0, [1, -1, 2])
print(list(it))  # [1, 2]
# ここで it は空。再利用不可
Python

判定関数内の例外に注意

int 変換や辞書アクセスなど、判定中に例外が起こり得ます。安全にするには try/except を入れて、異常系は False を返す設計が堅牢です。

def is_valid_int(s):
    try:
        int(s)
        return True
    except (TypeError, ValueError):
        return False

data = ["10", "x", None, "25"]
valid_strings = list(filter(is_valid_int, data))
print(valid_strings)  # ['10', '25']
Python

性能の考え方

filter は要素数に比例して O(n) で走査します。判定関数が重い場合はコストが支配的になるため、先に軽い条件で粗く絞る、早期に False を返せる順序にする、前処理を一回のパスにまとめる(map と併用)などでコストを抑えます。


実用例(現場でそのまま使える形)

ログ行から特定レベルだけ抽出

logs = ["INFO: start", "ERROR: failed", "WARN: slow", "ERROR: retry"]
errors = list(filter(lambda s: s.startswith("ERROR:"), logs))
print(errors)  # ['ERROR: failed', 'ERROR: retry']
Python

価格帯で商品を選ぶ(範囲判定)

products = [
    {"name": "A", "price": 300},
    {"name": "B", "price": 500},
    {"name": "C", "price": 800},
]
mid = list(filter(lambda p: 300 <= p["price"] <= 600, products))
print(mid)  # A, B
Python

ユーザー一覧から有効メールのみ抽出(超簡易チェック)

users = [{"name": "taro", "email": "taro@example.com"},
         {"name": "hanako", "email": ""}]
valid = list(filter(lambda u: "@" in u.get("email", ""), users))
print(valid)  # taroのみ
Python

まとめ

filter は「判定関数で True になる要素だけを通す」ためのシンプルで強力な関数です。戻り値はイテレータで一度きり、function=None で“真な要素”だけを通す、例外は判定関数で安全に処理する——この要点を押さえれば、欠損除去・前処理後の選別・ログ抽出・レコードフィルタなどを短く明快に書けます。条件付きの表現が複雑なら内包表記、関数適用が中心なら filter と使い分けることで、初心者でも読みやすく堅牢なデータ処理が実装できます。

タイトルとURLをコピーしました