【どちらを使うべき?】Ruby2.7から登場したfilter_mapと、map/filterの違いを丁寧に解説
はじめに
ruby2.7から filter_mapメソッド が利用できるようになりました。
Rubocopでもruby2.7以上で Performance/MapCompact が適用されています。
本記事では、 filter_map
と map
/ filter
の違いを徹底解説します。
TL;DR - 表でまとめると
本記事では「(配列の)要素」「メソッド」「ブロック」「(ブロックの)結果」「返り値」という単語を以下のように定義し、使用します。
[要素1, 要素2, 要素3,,,].メソッド { ブロック }
# ブロックが最後に評価した値を「(ブロックの)結果」と呼ぶ
=> 返り値
filter
/ map
/ filter_map
の違いを表でまとめると
filter | map | filter_map | |
---|---|---|---|
返り値 | 結果が真のみの要素の配列 | 結果の配列 ※結果の真偽は問わない | 結果が真のみの結果の配列 |
具体例
filter: 結果が真のみの要素の配列を返す
[1, 2, 3, 4, 5].filter { |i| i.even? }
=> [2, 4] # 要素が偶数なのは2と4だけ
[1, 2, 3, 4, 5].filter do |i|
double = i*2
double.even?
end
=> [1, 2, 3, 4, 5] # 要素の2倍が偶数なのは 1~5 のすべて
map: 結果の配列を返す ※結果の真偽は問わない
[1 ,2, 3, 4, 5].map { |i| i*2 }
=> [2, 4, 6, 8, 10] # 要素を2倍した結果を全て返す
[1, 2, 3, 4, 5].map { |i| i.even? }
=> [false, true, false, true, false] # 要素が偶数かの評価結果を全て返す
ここで問題です
以下の返り値を説明できますか?
問題1
[1, 2, 3, 4, 5].map do |i|
if i.even?
i*2
else
'偶数じゃない'
end
end
=> ["偶数じゃない", 4, "偶数じゃない", 8, "偶数じゃない"]
問題1の答え
問題1の答えはこちら
【要素の2,4】
2,4は偶数であるため、if文の結果がtrueになる
↓
if節を評価した結果はそれぞれ4,8になる
↓
そのまま返り値になる
【要素の1,3,5】
1,3,5は偶数でないため、if文の結果がfalseになり、else節に入る
↓
else節の結果は "偶数じゃない" になる
↓
そのまま返り値になる
問題2
[1, 2, 3, 4, 5].map do |i|
if i.even?
i*2
end
end
=> [nil, 4, nil, 8, nil]
[1, 2, 3, 4, 5].map { |i| i*2 if i.even? }
=> [nil, 4, nil, 8, nil]
答えの前にRubyの前提知識
if文の返り値
if式は、条件が成立した節(あるいは else 節)の最後に評価した式の結果を返します。 else 節がなくいずれの条件も成り立たなければ nil を返します。
後置ifの返り値
右辺の条件が成立する時に、左辺の式を評価してその結果を返します。 条件が成立しなければ nil を返します。
問題2の答え
問題2の答えはこちら
【要素の2,4】
2,4は偶数であるため、if文の結果がtrueになり、条件が成立する
↓
条件が成立したので、if節を評価し、結果はそれぞれ4,8になる
↓
そのまま返り値になる
【要素の1,3,5】
1,3,5は偶数でないため、if文の結果がfalseになり、条件が成立しない
↓
条件が成立しなかったので、結果はnilになる
↓
そのまま返り値になる
本題の前にもうひとつRubyの前提知識
false は nil オブジェクトとともに偽を表し、その他の全てのオブジェクトは真です。
やっと本題
filter_map: 結果が真のみの結果の配列を返す
[1, 2, 3, 4, 5].filter_map { |i| i*2 }
=> [2, 4, 6, 8, 10] # 要素を2倍した結果は全て真(すなわちfalseでもnilでもない)
[1, 2, 3, 4, 5].filter_map { |i| i.even? }
=> [true, true] # 偶数でない要素の結果はfalseとなり、falseは偽なので返り値から除かれる
問題3
以下の返り値を説明してください。
[1, 2, 3, 4, 5].filter_map { |i| i*2 if i.even? }
=> [4, 8]
問題3の答え
問題3の答えはこちら
【要素の2,4】
2,4は偶数であるため、後置if文の結果がtrueになり、条件が成立する
↓
条件が成立したので、左辺の式を評価し、結果はそれぞれ4,8になる
↓
4と8は真なので、返り値になる
【要素の1,3,5】
1,3,5は偶数でないため、後置if文の結果がfalseになり、条件が成立しない
↓
条件が成立しなかったので、結果はnilになる
↓
nilは偽なので、返り値から除かれる
最終問題
以下の返り値を説明してください。
[1, 2, 3, 4, 5].filter_map do |i|
if i.even?
i*2
else
'偶数じゃない'
end
end
=> ["偶数じゃない", 4, "偶数じゃない", 8, "偶数じゃない"]
最終問題の答え
最終問題の答えはこちら
【要素の2,4】
2,4は偶数であるため、if文の結果がtrueになる
↓
if節を評価した結果はそれぞれ4,8になる
↓
4と8は真なので、返り値になる
【要素の1,3,5】
1,3,5は偶数でないため、if文の結果がfalseになり、else節に入る
↓
else節の結果は "偶数じゃない" になる
↓
"偶数じゃない" は真なので、返り値になる
さいごに
最後まで読んでいただきありがとうございました!
宜しければぜひフォローお願いします!
Rubyってたのしいな💕
Discussion