Project

General

Profile

Actions

Feature #20899

open

Reconsider adding `Array#find_map`

Added by toy (Ivan Kuchin) 3 days ago. Updated 2 days ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:119944]

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

Also available in: Atom PDF

Like0
Like0Like0Like0Like0