map と filter を組み合わせる
条件と変換を 1 本の for に詰め込むと読めない
在庫のある商品だけを取り出して、名前の一覧を作りたいとします。for で書くと、空の入れ物を用意して、条件で弾いて、変換して、詰める、という 4 つの仕事が 1 つのループの中で混ざります。
JavaScript
const names = [];
for (const item of items) {
if (item.stock > 0) {
names.push(item.name);
}
}動きはします。ただし後から「条件だけ変えたい」と思ったとき、どの行が条件でどの行が変換なのかを目で探すことになります。行が増えるほど、この仕分けに時間がかかります。
絞ってから変える
道具は 2 つです。filter は条件に合う要素だけを残した新しい配列を返し、map は各要素を変換した新しい配列を返します。
JavaScript
const items = [
{ name: "マグカップ", stock: 3 },
{ name: "ノート", stock: 0 },
{ name: "ペン", stock: 12 },
];
items.filter(item => item.stock > 0);
// [{ name: "マグカップ", stock: 3 }, { name: "ペン", stock: 12 }]
items.map(item => item.name);
// ["マグカップ", "ノート", "ペン"]どちらも配列を返すので、そのままつなげられます。「在庫があるものに絞って、名前に変える」と言葉にした順に並べれば、そのままコードになります。
JavaScript
const names = items.filter(item => item.stock > 0).map(item => item.name);
// ["マグカップ", "ペン"]Python でも「条件に合うものを残す」「残ったものを変換する」という 2 つの役割は同じです。短縮構文を使わず、普通の for と if でも表せます。Go でも for で書けます。
覚え方 ... 「絞る」が
filter、「変える」がmap。日本語で言えた順に並べる。
順番を逆にすると答えが変わる
filter してから map と、map してから filter は別物です。「税込みにしてから 1,000 円以上を残す」と「1,000 円以上を残してから税込みにする」では、税抜き 950 円の商品の行方が変わります。つなぐ前に、絞る条件が変換の前の値に対するものか、後の値に対するものかを確かめてください。
元の配列は書き換わらない
filter も map も新しい配列を返すだけで、渡された配列には触れません。for の中で items[i] = ... と書くやり方だと、呼び出し側が持っている配列まで変わってしまい、離れた場所で原因の分からない不具合になります。空の配列を渡したときも、どちらも空の配列を返すだけで、例外にはなりません。
Python では条件と変換を別の行にする
同じ商品の例を普通のループで書くと、残す条件と取り出す情報が見えます。
Python
items = [
{"name": "マグカップ", "stock": 3},
{"name": "ノート", "stock": 0},
{"name": "ペン", "stock": 12},
]
names = []
for item in items:
if item["stock"] > 0:
names.append(item["name"])
print(names) # ["マグカップ", "ペン"]条件の行が「絞る」、追加する値の式が「変える」に当たります。演習では商品の代わりに整数を使い、条件と変換の式を自分で決めます。新しい配列の確保と返す行は準備済みです。
要件
- 偶数の要素だけを残し、それぞれを 2 倍した新しい配列を返す
- 元の配列 nums は変更しないこと
- 空配列が渡された場合は空配列を返す