Skip to content
64 changes: 64 additions & 0 deletions spec/std/indexable_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,70 @@ describe Indexable do
end
end

describe "#find" do
it "finds the element matching the block" do
indexable = SafeIndexable.new(4)
indexable.find { |i| i > 2 }.should eq 3
end

it "finds the element matching the block after given offset" do
indexable = SafeIndexable.new(8)
indexable.find(offset: 5) { |i| i.even? }.should eq 6
end

it "finds the element matching the block after given negative offset" do
indexable = SafeIndexable.new(8)
indexable.find(offset: -6) { |i| i.even? }.should eq 2
end

it "does not receive a valid negative offset, returns nil" do
indexable = SafeIndexable.new(4)
indexable.find(offset: -10) { |i| i > 2 }.should be_nil
end

it "does not find the element matching the block" do
indexable = SafeIndexable.new(4)
indexable.find { |i| i > 7 }.should be_nil
end

it "does not find the element matching the block, returns custom if_none value" do
indexable = SafeIndexable.new(4)
indexable.find(if_none: -1) { |i| i > 7 }.should eq -1
end

it "does not find the element matching the block after given offset, returns custom if_none value" do
indexable = SafeIndexable.new(5)
indexable.find(-3, 3) { |i| i > 15 }.should eq -3
end
end

describe "#find!" do
it "finds the element matching the block" do
indexable = SafeIndexable.new(4)
indexable.find! { |i| i > 2 }.should eq 3
end

it "finds the element matching the block after given offset" do
indexable = SafeIndexable.new(8)
indexable.find!(offset: 5) { |i| i.even? }.should eq 6
end

it "finds the element matching the block after given negative offset" do
indexable = SafeIndexable.new(8)
indexable.find!(offset: -6) { |i| i.even? }.should eq 2
end

it "does not receive a valid negative offset, raises not found" do
indexable = SafeIndexable.new(4)
expect_raises(Enumerable::NotFoundError) { indexable.find!(offset: -10) { |i| i > 2 } }
end

it "does not find the element matching the block, raises not found" do
indexable = SafeIndexable.new(4)
expect_raises(Enumerable::NotFoundError) { indexable.find! { |i| i > 7 } }
end
end

describe "#rindex" do
it "does rindex with big negative offset" do
indexable = SafeIndexable.new(3)
Expand Down
35 changes: 35 additions & 0 deletions src/indexable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,41 @@ module Indexable(T)
index(offset) { |e| yield e } || raise Enumerable::NotFoundError.new
end

# Returns the first element in the indexable for which the passed block
# is truthy, starting from the given *offset*.
#
# Accepts an optional parameter *if_none*, to set what gets returned if
# no element is found (defaults to `nil`).
#
# ```
# [1, 2, 3, 4].find { |i| i > 2 } # => 3
# [1, 2, 3, 4].find(-1) { |i| i > 8 } # => -1
# [1, 2, 3, 4].find(-1, 2) { |i| i < 2 } # => -1
# ```
def find(if_none = nil, offset : Int = 0, & : T ->)
offset += size if offset < 0
return nil if offset < 0

if idx = index(offset) { |i| yield i }
return unsafe_fetch(idx)
end
if_none
end

# Returns the first element in the indexable for which the passed block
# is truthy, starting from the given *offset*.
# Raises `Enumerable::NotFoundError` if there is no element for which the block is truthy.
#
# ```
# [1, 2, 3, 4].find! { |i| i > 2 } # => 3
# [1, 2, 3, 4].find!(3) { |i| i > 2 } # => 4
# [1, 2, 3, 4].find! { |i| i > 8 } # => raises Enumerable::NotFoundError
# [1, 2, 3, 4].find!(-5) { |i| i > 2 } # => raises Enumerable::NotFoundError
# ```
def find!(offset : Int = 0, & : T ->)
find(offset: offset) { |i| yield i } || raise Enumerable::NotFoundError.new
end

# Returns the last element of `self` if it's not empty, or raises `IndexError`.
#
# ```
Expand Down
Loading