From 42ba3136eb5fdb918a53b13e713d4f5cae4e50e3 Mon Sep 17 00:00:00 2001 From: Prateek Singh Date: Wed, 12 Mar 2025 23:51:10 -0700 Subject: [PATCH 1/2] add find and find! methods to indexable --- spec/std/indexable_spec.cr | 44 ++++++++++++++++++++++++++++++++++++++ src/indexable.cr | 29 +++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/spec/std/indexable_spec.cr b/spec/std/indexable_spec.cr index 2d1ab60c0846..bf01b0e23f62 100644 --- a/spec/std/indexable_spec.cr +++ b/spec/std/indexable_spec.cr @@ -143,6 +143,50 @@ 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(5) { |i| i.even? }.should eq 6 + 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!(5) { |i| i.even? }.should eq 6 + 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) diff --git a/src/indexable.cr b/src/indexable.cr index 3f6dca1762b1..47547169929a 100644 --- a/src/indexable.cr +++ b/src/indexable.cr @@ -809,6 +809,35 @@ 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, 2) { |i| i < 2 } # => -1 + # [1, 2, 3, 4].find(-1) { |i| i > 8 } # => -1 + # ``` + def find(offset : Int = 0, if_none = nil, & : T ->) + offset += size if offset < 0 + return nil if offset < 0 + return (index(offset) { |i| yield i }).try { |i| unsafe_fetch(i) } || if_none + end + + # Returns the first element in the indexable for which the passed block is truthy. + # 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! { |i| i > 8 } # => raises Enumerable::NotFoundError + def find!(offset : Int = 0, & : T ->) + offset += size if offset < 0 + return nil if offset < 0 + return (index(offset) { |i| yield i }).try { |i| unsafe_fetch(i) } || raise Enumerable::NotFoundError.new + end + # Returns the last element of `self` if it's not empty, or raises `IndexError`. # # ``` From 48263357c62eee2d761edc2b806a072f03131310 Mon Sep 17 00:00:00 2001 From: Prateek Singh Date: Thu, 13 Mar 2025 10:47:38 -0700 Subject: [PATCH 2/2] address comment, linting --- spec/std/indexable_spec.cr | 2 +- src/indexable.cr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/std/indexable_spec.cr b/spec/std/indexable_spec.cr index bf01b0e23f62..de929ba3e848 100644 --- a/spec/std/indexable_spec.cr +++ b/spec/std/indexable_spec.cr @@ -169,7 +169,7 @@ describe Indexable do 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) diff --git a/src/indexable.cr b/src/indexable.cr index 47547169929a..3531a105454a 100644 --- a/src/indexable.cr +++ b/src/indexable.cr @@ -835,7 +835,7 @@ module Indexable(T) def find!(offset : Int = 0, & : T ->) offset += size if offset < 0 return nil if offset < 0 - return (index(offset) { |i| yield i }).try { |i| unsafe_fetch(i) } || raise Enumerable::NotFoundError.new + find(offset) { |i| yield i } || raise Enumerable::NotFoundError.new end # Returns the last element of `self` if it's not empty, or raises `IndexError`.