🔖
[Feature #8421] Enumerable#find_map を追加する提案
[Feature #8421] add Enumerable#find_map and Enumerable#find_all_map
-
#find
のブロックの戻り値がそのままメソッドの戻り値となるようなEnumerable#find_map
を追加する提案 - イメージ以下のような感じ
# user.lazy.filter_map{ _1.profile }.first と同じような感じ
result = users.find_map { _1.profile }
- これ自体は11年前にできたチケットなんですが、暫くユースケースが提示されていなくて特に進展はなかったんですが、最近以下のようなユースケースが投稿されていました
# 特定の条件に当てはまったときに任意の値を返したい例
# Option 1
def identifier(emails, pattern: /\Ausername\+(?<identifier>[a-z|0-9]+)@domain\.com\z/i)
result = nil
emails.each do |email|
if matches = pattern.match(email)
result = matches[:identifier]
break
end
end
result
end
# Option 2
def identifier(emails, pattern: /\Ausername\+(?<identifier>[a-z|0-9]+)@domain\.com\z/i)
matches = nil
matches[:identifier] if emails.find { |email| matches = pattern.match(email) }
end
- 上記のようなコードは
#find_map
だと以下のように簡略化できるとコメントされています
def find_map(collection, &block)
result = nil
collection.each do |item|
break if result = yield(item)
end
result
end
def identifier(emails, pattern: /\Ausername\+(?<identifier>[a-z|0-9]+)@domain\.com\z/i)
# 条件を書きつつ、任意の値を返すような処理になる
find_map(emails) do |email|
(matches = pattern.match(email)) && matches[:identifier]
end
end
- さらに上のコードに対して
#find_map
を利用しなくても次のように書くこともできるとコメントもされていますね
# proposal:
find_map(emails) do |email|
(matches = pattern.match(email)) && matches[:identifier]
end
# break を使って任意のタイミングでブロックを抜けて、任意の値を返す
emails.find { |email| match = pattern.match(email) and break match[:identifier] }
# => "thecode"
- 他には以下のような書き方も
emails.find{ pattern.match(it) }&.then{ it[:identifier] }
- いろいろと解決方法がありますねー
Discussion