Skip to content

Commit 6147b69

Browse files
committed
Array#rfind
Implement Array#rfind, which is the same as find except from the other side of the Array. Also implemented Array#find (as opposed to the generic one on Enumerable because it is significantly faster and to keep the implementations together. [Feature #21678]
1 parent 4f900e3 commit 6147b69

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed

array.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,6 +2088,99 @@ rb_ary_fetch(int argc, VALUE *argv, VALUE ary)
20882088
return RARRAY_AREF(ary, idx);
20892089
}
20902090

2091+
/*
2092+
* call-seq:
2093+
* find(if_none_proc = nil) {|element| ... } -> object or nil
2094+
* find(if_none_proc = nil) -> enumerator
2095+
*
2096+
* Returns the first element for which the block returns a truthy value.
2097+
*
2098+
* With a block given, calls the block with successive elements of the array;
2099+
* returns the first element for which the block returns a truthy value:
2100+
*
2101+
* (0..9).find {|element| element > 2} # => 3
2102+
*
2103+
* If no such element is found, calls +if_none_proc+ and returns its return value.
2104+
*
2105+
* (0..9).find(proc {false}) {|element| element > 12} # => false
2106+
* {foo: 0, bar: 1, baz: 2}.find {|key, value| key.start_with?('b') } # => [:bar, 1]
2107+
* {foo: 0, bar: 1, baz: 2}.find(proc {[]}) {|key, value| key.start_with?('c') } # => []
2108+
*
2109+
* With no block given, returns an Enumerator.
2110+
*
2111+
*/
2112+
2113+
static VALUE
2114+
rb_ary_find(int argc, VALUE *argv, VALUE ary)
2115+
{
2116+
VALUE if_none;
2117+
long idx;
2118+
2119+
RETURN_ENUMERATOR(ary, argc, argv);
2120+
if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;
2121+
2122+
for (idx = 0; idx < RARRAY_LEN(ary); idx++) {
2123+
VALUE elem = RARRAY_AREF(ary, idx);
2124+
if (RTEST(rb_yield(elem))) {
2125+
return elem;
2126+
}
2127+
}
2128+
2129+
if (!NIL_P(if_none)) {
2130+
return rb_funcallv(if_none, idCall, 0, 0);
2131+
}
2132+
return Qnil;
2133+
}
2134+
2135+
/*
2136+
* call-seq:
2137+
* rfind(if_none_proc = nil) {|element| ... } -> object or nil
2138+
* rfind(if_none_proc = nil) -> enumerator
2139+
*
2140+
* Returns the last element for which the block returns a truthy value.
2141+
*
2142+
* With a block given, calls the block with successive elements of the array in
2143+
* reverse order; returns the last element for which the block returns a truthy
2144+
* value:
2145+
*
2146+
* (0..9).rfind {|element| element < 5} # => 4
2147+
*
2148+
* If no such element is found, calls +if_none_proc+ and returns its return value.
2149+
*
2150+
* (0..9).rfind(proc {false}) {|element| element < -2} # => false
2151+
* {foo: 0, bar: 1, baz: 2}.rfind {|key, value| key.start_with?('b') } # => [:baz, 2]
2152+
* {foo: 0, bar: 1, baz: 2}.rfind(proc {[]}) {|key, value| key.start_with?('c') } # => []
2153+
*
2154+
* With no block given, returns an Enumerator.
2155+
*
2156+
*/
2157+
2158+
static VALUE
2159+
rb_ary_rfind(int argc, VALUE *argv, VALUE ary)
2160+
{
2161+
VALUE if_none;
2162+
long len, idx;
2163+
2164+
RETURN_ENUMERATOR(ary, argc, argv);
2165+
if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil;
2166+
2167+
idx = RARRAY_LEN(ary);
2168+
while (idx--) {
2169+
VALUE elem = RARRAY_AREF(ary, idx);
2170+
if (RTEST(rb_yield(elem))) {
2171+
return elem;
2172+
}
2173+
2174+
len = RARRAY_LEN(ary);
2175+
idx = (idx >= len) ? len : idx;
2176+
}
2177+
2178+
if (!NIL_P(if_none)) {
2179+
return rb_funcallv(if_none, idCall, 0, 0);
2180+
}
2181+
return Qnil;
2182+
}
2183+
20912184
/*
20922185
* call-seq:
20932186
* find_index(object) -> integer or nil
@@ -8816,6 +8909,8 @@ Init_Array(void)
88168909
rb_define_method(rb_cArray, "length", rb_ary_length, 0);
88178910
rb_define_method(rb_cArray, "size", rb_ary_length, 0);
88188911
rb_define_method(rb_cArray, "empty?", rb_ary_empty_p, 0);
8912+
rb_define_method(rb_cArray, "find", rb_ary_find, -1);
8913+
rb_define_method(rb_cArray, "rfind", rb_ary_rfind, -1);
88198914
rb_define_method(rb_cArray, "find_index", rb_ary_index, -1);
88208915
rb_define_method(rb_cArray, "index", rb_ary_index, -1);
88218916
rb_define_method(rb_cArray, "rindex", rb_ary_rindex, -1);

test/ruby/test_array.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3584,6 +3584,23 @@ def test_array_safely_modified_by_sort_block
35843584
assert_equal((1..67).to_a.reverse, var_0)
35853585
end
35863586

3587+
def test_find
3588+
ary = [1, 2, 3, 4, 5]
3589+
assert_equal(2, ary.find {|x| x % 2 == 0 })
3590+
assert_equal(nil, ary.find {|x| false })
3591+
assert_equal(:foo, ary.find(proc { :foo }) {|x| false })
3592+
end
3593+
3594+
def test_rfind
3595+
ary = [1, 2, 3, 4, 5]
3596+
assert_equal(4, ary.rfind {|x| x % 2 == 0 })
3597+
assert_equal(1, ary.rfind {|x| x < 2 })
3598+
assert_equal(5, ary.rfind {|x| x > 4 })
3599+
assert_equal(nil, ary.rfind {|x| false })
3600+
assert_equal(:foo, ary.rfind(proc { :foo }) {|x| false })
3601+
assert_equal(nil, ary.rfind {|x| ary.clear; false })
3602+
end
3603+
35873604
private
35883605
def need_continuation
35893606
unless respond_to?(:callcc, true)

0 commit comments

Comments
 (0)