mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-array-ext: add Array#find and Array#rfind
Array#find is an optimized version of Enumerable#find for arrays, using direct index access instead of each iterator. Array#rfind finds from the end of the array, returning the first match when scanning backwards. Both methods support the ifnone parameter for default values. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -726,4 +726,59 @@ class Array
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.find(ifnone = nil) { |elem| block } -> obj or nil
|
||||
# ary.find(ifnone = nil) -> Enumerator
|
||||
#
|
||||
# Returns the first element for which the block returns a true value.
|
||||
# If no element matches and +ifnone+ is given, calls +ifnone+ and
|
||||
# returns its result. Otherwise returns +nil+.
|
||||
#
|
||||
# This is an optimized version of Enumerable#find for arrays.
|
||||
#
|
||||
# [1, 2, 3, 4].find { |x| x > 2 } #=> 3
|
||||
# [1, 2, 3, 4].find { |x| x > 10 } #=> nil
|
||||
# [1, 2, 3, 4].find(->{0}) { |x| x > 10 } #=> 0
|
||||
#
|
||||
def find(ifnone=nil, &block)
|
||||
return to_enum(:find, ifnone) unless block
|
||||
|
||||
idx = 0
|
||||
len = self.size
|
||||
while idx < len
|
||||
elem = self[idx]
|
||||
return elem if block.call(elem)
|
||||
idx += 1
|
||||
end
|
||||
ifnone&.call
|
||||
end
|
||||
|
||||
##
|
||||
# call-seq:
|
||||
# ary.rfind(ifnone = nil) { |elem| block } -> obj or nil
|
||||
# ary.rfind(ifnone = nil) -> Enumerator
|
||||
#
|
||||
# Returns the last element for which the block returns a true value.
|
||||
# Searches from the end of the array to the beginning.
|
||||
# If no element matches and +ifnone+ is given, calls +ifnone+ and
|
||||
# returns its result. Otherwise returns +nil+.
|
||||
#
|
||||
# [1, 2, 3, 4, 3].rfind { |x| x == 3 } #=> 3 (the last one)
|
||||
# [1, 2, 3, 4].rfind { |x| x > 2 } #=> 4
|
||||
# [1, 2, 3, 4].rfind { |x| x > 10 } #=> nil
|
||||
# [1, 2, 3, 4].rfind(->{0}) { |x| x > 10 } #=> 0
|
||||
#
|
||||
def rfind(ifnone=nil, &block)
|
||||
return to_enum(:rfind, ifnone) unless block
|
||||
|
||||
idx = self.size - 1
|
||||
while idx >= 0
|
||||
elem = self[idx]
|
||||
return elem if block.call(elem)
|
||||
idx -= 1
|
||||
end
|
||||
ifnone&.call
|
||||
end
|
||||
end
|
||||
|
||||
@@ -782,3 +782,68 @@ assert("Array#deconstruct") do
|
||||
assert_equal([[1, 2], [3, 4], [5]], result_nested)
|
||||
assert_true(result_nested.equal?(d))
|
||||
end
|
||||
|
||||
assert("Array#find") do
|
||||
# Basic find
|
||||
assert_equal 3, [1, 2, 3, 4, 5].find { |x| x > 2 }
|
||||
assert_equal 1, [1, 2, 3, 4, 5].find { |x| x < 2 }
|
||||
|
||||
# No match returns nil
|
||||
assert_nil [1, 2, 3].find { |x| x > 10 }
|
||||
|
||||
# Empty array
|
||||
assert_nil [].find { |x| x > 0 }
|
||||
|
||||
# With ifnone callable
|
||||
assert_equal 0, [1, 2, 3].find(->{ 0 }) { |x| x > 10 }
|
||||
assert_equal "default", [1, 2, 3].find(->{ "default" }) { |x| x > 10 }
|
||||
|
||||
# ifnone not called when match found
|
||||
called = false
|
||||
[1, 2, 3].find(->{ called = true; 0 }) { |x| x == 2 }
|
||||
assert_false called
|
||||
|
||||
# Returns first match
|
||||
assert_equal 2, [1, 2, 2, 3].find { |x| x == 2 }
|
||||
|
||||
# Works with different types
|
||||
assert_equal "b", ["a", "b", "c"].find { |x| x == "b" }
|
||||
assert_equal :bar, [:foo, :bar, :baz].find { |x| x == :bar }
|
||||
end
|
||||
|
||||
assert("Array#rfind") do
|
||||
# Basic rfind - finds from end (first match scanning backwards)
|
||||
assert_equal 5, [1, 2, 3, 4, 5].rfind { |x| x > 2 } # 5 is first match from end
|
||||
assert_equal 5, [1, 2, 3, 4, 5].rfind { |x| x > 0 } # 5 is first match from end
|
||||
|
||||
# Returns last occurrence when duplicates exist
|
||||
a = [1, 2, 3, 2, 1]
|
||||
assert_equal 2, a.rfind { |x| x == 2 } # finds the 2 at index 3
|
||||
|
||||
# No match returns nil
|
||||
assert_nil [1, 2, 3].rfind { |x| x > 10 }
|
||||
|
||||
# Empty array
|
||||
assert_nil [].rfind { |x| x > 0 }
|
||||
|
||||
# With ifnone callable
|
||||
assert_equal 0, [1, 2, 3].rfind(->{ 0 }) { |x| x > 10 }
|
||||
assert_equal "default", [1, 2, 3].rfind(->{ "default" }) { |x| x > 10 }
|
||||
|
||||
# ifnone not called when match found
|
||||
called = false
|
||||
[1, 2, 3].rfind(->{ called = true; 0 }) { |x| x == 2 }
|
||||
assert_false called
|
||||
|
||||
# Compare find vs rfind - same result for unique match
|
||||
arr = [1, 2, 3, 4, 3, 2, 1]
|
||||
assert_equal 3, arr.find { |x| x == 3 } # first 3 (index 2)
|
||||
assert_equal 3, arr.rfind { |x| x == 3 } # last 3 (index 4), same value
|
||||
|
||||
# Different results with inequality - rfind scans from end
|
||||
assert_equal 3, arr.find { |x| x >= 3 } # first >= 3 is 3 (at index 2)
|
||||
assert_equal 3, arr.rfind { |x| x >= 3 } # scanning from end: 1,2,3 - 3 matches first
|
||||
|
||||
# Works with different types
|
||||
assert_equal "b", ["a", "b", "c", "b", "a"].rfind { |x| x > "a" } # scanning from end: a,b - b matches
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user