From: "alexbarret (Alexandre Barret) via ruby-core" Date: 2024-03-19T20:38:37+00:00 Subject: [ruby-core:117237] [Ruby master Feature#8421] add Enumerable#find_map and Enumerable#find_all_map Issue #8421 has been updated by alexbarret (Alexandre Barret). Can we reconsider introducing `#find_map` please, especially since `#find_all_map` has been introduced as `#filter_map` in Ruby in 2.7? Here are some examples ```ruby require "minitest/autorun" # Option 1 def identifier(emails, pattern: /\Ausername\+(?[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\+(?[a-z|0-9]+)@domain\.com\z/i) matches = nil matches[:identifier] if emails.find { |email| matches = pattern.match(email) } end class TestIdentifierMethod < Minitest::Test def test_identifier assert_equal 'thecode', identifier(%w[ username@domain.com username+123@domainAcom wrongusername+123@domain.com username+123@wrongdomain.com username+thecode@domain.com ]) assert_nil identifier(%w[ username@domain.com username+123@domainAcom wrongusername+123@domain.com username+123@wrongdomain.com ]) end end ``` Having a find_map would ease it a bit ```ruby def find_map(collection, &block) result = nil collection.each do |item| break if result = yield(item) end result end def identifier(emails, pattern: /\Ausername\+(?[a-z|0-9]+)@domain\.com\z/i) find_map(emails) do |email| (matches = pattern.match(email)) && matches[:identifier] end end ``` Here is a second use case ```ruby # Problem 2 Pet = Struct.new(:name) Person = Struct.new(:name, :pet, keyword_init: true) class TestPetIdentitifer < Minitest::Test def setup @some_people_with_pet = [ Person.new(name: 'Alex', pet: nil), Person.new(name: 'Olivier', pet: nil), Person.new(name: 'Romain', pet: Pet.new('Darwin')), Person.new(name: 'Mariano', pet: nil), Person.new(name: 'S�bastien', pet: nil), Person.new(name: 'Ben', pet: nil) ] @people_with_no_pet = [ Person.new(name: 'Mariano', pet: nil), Person.new(name: 'S�bastien', pet: nil), Person.new(name: 'Ben', pet: nil) ] end def test_pet_found people = @some_people_with_pet expected_pet = Pet.new('Darwin') assert_equal expected_pet, people.find(&:pet)&.pet assert_equal expected_pet, find_map(people, &:pet) # -> people.find_map(&:pet) end def test_pet_not_found people = @people_with_no_pet assert_nil people.find(&:pet)&.pet assert_nil find_map(people, &:pet) # -> people.find_map(&:pet) end end ``` Having `#find_map` allows these benefits * The caller does not need to guard against `nil` like when `#find` returns nothing * `#find_map` would be faster than `filter_map.first` or even `lazy.filter_map.first` * It would add the parity with `filter_map`. `#find` is to `#filter` what `#find_map` is to `#filter_map` ---------------------------------------- Feature #8421: add Enumerable#find_map and Enumerable#find_all_map https://bugs.ruby-lang.org/issues/8421#change-107324 * Author: Hanmac (Hans Mackowiak) * Status: Feedback ---------------------------------------- currently if you have an Enumerable and you want to return the return value of #find you need eigther: (o = enum.find(block) && block.call(o)) || nil or enum.inject(nil) {|ret,el| ret || block.call(el)} neigher of them may be better than an directly maked method same for #find_all_map enum.lazy.map(&:block).find_all{|el| el} it may work but it is not so good -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/