Actions
Feature #20899
openReconsider adding `Array#find_map`
Status:
Open
Assignee:
-
Target version:
-
Description
I would like to retry proposing method Array#find_map
that was rejected in 8421 which happened before introduction of filter_map
in 15323.
It would make code nicer whenever there is a need to get the first truthy result of applying some code.
Adapting examples from filter_map
documentation, but if I need only the first value:
(1..9).find_map {|i| i * 2 if i.even? } # => 4
{foo: 0, bar: 1, baz: 2}.find_map {|key, value| key if value.even? } # => :foo
Or an example of getting match group for first successful match:
list = ['some 123', 'list 234', 'of 345', 'strings 456']
list.find_map{ |s| s[/\Aof (\d+)\z/, 1] } # => "345"
Currently I imagine either more code and/or inefficiency (extra calls and/or objects):
# code called twice
list.find{ |s| s[/\Aof (\d+)\z/, 1] }&.then{ |s| s[/\Aof (\d+)\z/, 1] } # => "345"
# more logic
result = nil
list.each do |s|
break if (result = s[/\Aof (\d+)\z/, 1])
end
result # => "345"
# or
result = nil
list.find do |s|
result = s[/\Aof (\d+)\z/, 1]
end
result # => "345"
# extra calls for items which come after item that we were looking for
list.map{ |s| s[/\Aof (\d+)\z/, 1] }.find{ _1 } # => "345"
# using lazy
list.lazy.map{ |s| s[/\Aof (\d+)\z/, 1] }.find{ _1 } # => "345"
# or as suggested by @alexbarret in https://bugs.ruby-lang.org/issues/8421?tab=history#note-7
list.lazy.filter_map{ |s| s[/\Aof (\d+)\z/, 1] }.first # => "345"
# using tricks, as suggested by @zverok in https://bugs.ruby-lang.org/issues/8421?tab=history#note-4
list.find{ |s| result = s[/\Aof (\d+)\z/, 1] and break result } # => "345"
Implementation in ruby can be:
Enumerable.class_eval do
def find_map(&block)
each do |element|
block_result = block.call(element)
return block_result if block_result
end
nil
end
end
An example from another language - scala method collect
works alike filter_map
and collectFirst
would be like find_map
.
Actions
Like0
Like0Like0Like0Like0