1. はじめに
Pythonで配列をフィルタリングしようとしたものの、
# メニューの中から、リンゴを使ったものだけ注文したい!🍎
menu_list = ["apple juice", "orange juice", "apple pie", "lemon pie", "lemon juice"]
order_list = []
for menu in menu_list:
if "apple" in menu:
order_list.append(menu)
print(order_list) # ["apple juice", "apple pie"]
のように愚直にfor
ループを回す方法しか思いつかなかった。
order_list = [menu for menu in menu_list if "apple" in menu]
とすると1行で書くことはできるけれど、結局for
ループを回していることに変わりはないし、もっといい書き方をしたい。
ずっとPythonを書いているのにフィルタリング方法を知らなかったことに驚きつつ調べたところ、便利な組み込み関数filter()
が見つかった。
2. 基本的な使い方
filter(関数, iterableなオブジェクト)
という書き方をする。関数
の戻り値がTrue
となる要素のみをもつ、filter
クラスのコレクションを生成してくれる。
3. 例
3. 1. 配列
例えば、最初の例は
def is_apple_menu_list(menu):
is_apple = "apple" in menu
return is_apple
filtered_menu_list = filter(is_apple_menu_list, menu_list)
と書ける。ただし、戻り値は配列ではなくfilter
クラスのオブジェクトである。
print(filtered_menu_list) # <filter object at 0x7f468071f940>
print(type(filtered_menu_list)) # <class 'filter'>
これは、range
クラスと同様にイテラブルなオブジェクトなので、range()
を使う場合と同じようにfor
ループを回して中身を取り出すことができる。
for menu in filtered_menu_list:
print(menu)
# apple juice
# apple pie
配列へ変換するには
print(list(filtered_menu_list)) # ["apple juice", "apple pie"]
filter()
の第一引数にlambda関数を用いるとシンプルに書ける。
filtered_menu_list = filter(lambda menu: "apple" in menu, menu_list)
タブルも配列と同様の書き方でフィルタリングできる。
3. 2. 辞書
以下のような辞書があるとする。
menu_dict = {
"apple juice": 300,
"orange juice": 250,
"apple pie": 450,
"lemon pie": 400,
"lemon juice": 200,
}
同様に"apple"
でフィルタリングするには
def is_apple_menu_dict(menu):
is_apple = "apple" in menu[0]
return is_apple
filtered_menu_dict = filter(is_apple_menu_dict, menu_dict.items())
または
filtered_menu_dict = filter(lambda menu: "apple" in menu[0], menu_dict.items())
となる。辞書型に戻すには、
print(dict(filtered_menu_dict)) # {"apple juice": 300, "apple pie": 450}
4. 注意点
Pythonのイテレータは、一度取り出すと元に戻せないので、例のようにfor
ループを回した後に配列や辞書に変換しようとすると、中身が空の配列や辞書が生成されてしまう。
filter()
を使って生成したコレクションを何度も使う場合は、先に配列や辞書などに変換しておいた方がよさそう。