Skip to content

Commit

Permalink
Add Enumerable#find_value (#14893)
Browse files Browse the repository at this point in the history
Co-authored-by: Sijawusz Pur Rahnama <sija@sija.pl>
  • Loading branch information
jgaskins and Sija authored Nov 5, 2024
1 parent 9f5533c commit 0cac615
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 0 deletions.
25 changes: 25 additions & 0 deletions spec/std/enumerable_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,31 @@ describe "Enumerable" do
end
end

describe "find_value" do
it "finds and returns the first truthy block result" do
[1, 2, 3].find_value { |i| "1" if i == 1 }.should eq "1"
{1, 2, 3}.find_value { |i| "2" if i == 2 }.should eq "2"
(1..3).find_value { |i| "3" if i == 3 }.should eq "3"

# Block returns `true && expression` vs the above `expression if true`.
# Same idea, but a different idiom. It serves as an allegory for the next
# test which checks `false` vs `nil`.
[1, 2, 3].find_value { |i| i == 1 && "1" }.should eq "1"
{1, 2, 3}.find_value { |i| i == 2 && "2" }.should eq "2"
(1..3).find_value { |i| i == 3 && "3" }.should eq "3"
end

it "returns the default value if there are no truthy block results" do
{1, 2, 3}.find_value { |i| "4" if i == 4 }.should eq nil
{1, 2, 3}.find_value "nope" { |i| "4" if i == 4 }.should eq "nope"
([] of Int32).find_value false { true }.should eq false

# Same as above but returns `false` instead of `nil`.
{1, 2, 3}.find_value { |i| i == 4 && "4" }.should eq nil
{1, 2, 3}.find_value "nope" { |i| i == 4 && "4" }.should eq "nope"
end
end

describe "first" do
it "calls block if empty" do
(1...1).first { 10 }.should eq(10)
Expand Down
19 changes: 19 additions & 0 deletions src/enumerable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,25 @@ module Enumerable(T)
raise Enumerable::NotFoundError.new
end

# Yields each value until the first truthy block result and returns that result.
#
# Accepts an optional parameter `if_none`, to set what gets returned if
# no element is found (defaults to `nil`).
#
# ```
# [1, 2, 3, 4].find_value { |i| i > 2 } # => true
# [1, 2, 3, 4].find_value { |i| i > 8 } # => nil
# [1, 2, 3, 4].find_value(-1) { |i| i > 8 } # => -1
# ```
def find_value(if_none = nil, & : T ->)
each do |i|
if result = yield i
return result
end
end
if_none
end

# Returns the first element in the collection,
# If the collection is empty, calls the block and returns its value.
#
Expand Down

0 comments on commit 0cac615

Please sign in to comment.