Skip to content

Commit 6bf8a3e

Browse files
committed
Merge pull request godotengine#95449 from SlashScreen/array_functions
Add callable support for `find` and `rfind` `Array` methods
2 parents da9764a + 89491f4 commit 6bf8a3e

File tree

5 files changed

+125
-1
lines changed

5 files changed

+125
-1
lines changed

core/variant/array.cpp

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,34 @@ int Array::find(const Variant &p_value, int p_from) const {
369369
return ret;
370370
}
371371

372+
int Array::find_custom(const Callable &p_callable, int p_from) const {
373+
int ret = -1;
374+
375+
if (p_from < 0 || size() == 0) {
376+
return ret;
377+
}
378+
379+
const Variant *argptrs[1];
380+
381+
for (int i = p_from; i < size(); i++) {
382+
const Variant &val = _p->array[i];
383+
argptrs[0] = &val;
384+
Variant res;
385+
Callable::CallError ce;
386+
p_callable.callp(argptrs, 1, res, ce);
387+
if (unlikely(ce.error != Callable::CallError::CALL_OK)) {
388+
ERR_FAIL_V_MSG(ret, "Error calling method from 'find_custom': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
389+
}
390+
391+
ERR_FAIL_COND_V_MSG(res.get_type() != Variant::Type::BOOL, ret, "Error on method from 'find_custom': Return type of callable must be boolean.");
392+
if (res.operator bool()) {
393+
return i;
394+
}
395+
}
396+
397+
return ret;
398+
}
399+
372400
int Array::rfind(const Variant &p_value, int p_from) const {
373401
if (_p->array.size() == 0) {
374402
return -1;
@@ -394,6 +422,41 @@ int Array::rfind(const Variant &p_value, int p_from) const {
394422
return -1;
395423
}
396424

425+
int Array::rfind_custom(const Callable &p_callable, int p_from) const {
426+
if (_p->array.size() == 0) {
427+
return -1;
428+
}
429+
430+
if (p_from < 0) {
431+
// Relative offset from the end.
432+
p_from = _p->array.size() + p_from;
433+
}
434+
if (p_from < 0 || p_from >= _p->array.size()) {
435+
// Limit to array boundaries.
436+
p_from = _p->array.size() - 1;
437+
}
438+
439+
const Variant *argptrs[1];
440+
441+
for (int i = p_from; i >= 0; i--) {
442+
const Variant &val = _p->array[i];
443+
argptrs[0] = &val;
444+
Variant res;
445+
Callable::CallError ce;
446+
p_callable.callp(argptrs, 1, res, ce);
447+
if (unlikely(ce.error != Callable::CallError::CALL_OK)) {
448+
ERR_FAIL_V_MSG(-1, "Error calling method from 'rfind_custom': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
449+
}
450+
451+
ERR_FAIL_COND_V_MSG(res.get_type() != Variant::Type::BOOL, -1, "Error on method from 'rfind_custom': Return type of callable must be boolean.");
452+
if (res.operator bool()) {
453+
return i;
454+
}
455+
}
456+
457+
return -1;
458+
}
459+
397460
int Array::count(const Variant &p_value) const {
398461
Variant value = p_value;
399462
ERR_FAIL_COND_V(!_p->typed.validate(value, "count"), 0);
@@ -761,7 +824,7 @@ Variant Array::max() const {
761824
return Variant(); //not a valid comparison
762825
}
763826
if (bool(ret)) {
764-
//is less
827+
//is greater
765828
maxval = test;
766829
}
767830
}

core/variant/array.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ class Array {
152152
void reverse();
153153

154154
int find(const Variant &p_value, int p_from = 0) const;
155+
int find_custom(const Callable &p_callable, int p_from = 0) const;
155156
int rfind(const Variant &p_value, int p_from = -1) const;
157+
int rfind_custom(const Callable &p_callable, int p_from = -1) const;
156158
int count(const Variant &p_value) const;
157159
bool has(const Variant &p_value) const;
158160

core/variant/variant_call.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,7 +2305,9 @@ static void _register_variant_builtin_methods_array() {
23052305
bind_method(Array, back, sarray(), varray());
23062306
bind_method(Array, pick_random, sarray(), varray());
23072307
bind_method(Array, find, sarray("what", "from"), varray(0));
2308+
bind_method(Array, find_custom, sarray("method", "from"), varray(0));
23082309
bind_method(Array, rfind, sarray("what", "from"), varray(-1));
2310+
bind_method(Array, rfind_custom, sarray("method", "from"), varray(-1));
23092311
bind_method(Array, count, sarray("value"), varray());
23102312
bind_method(Array, has, sarray("value"), varray());
23112313
bind_method(Array, pop_back, sarray(), varray());

doc/classes/Array.xml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@
324324
<param index="0" name="value" type="Variant" />
325325
<description>
326326
Returns the number of times an element is in the array.
327+
To count how many elements in an array satisfy a condition, see [method reduce].
327328
</description>
328329
</method>
329330
<method name="duplicate" qualifiers="const">
@@ -395,6 +396,25 @@
395396
[b]Note:[/b] For performance reasons, the search is affected by [param what]'s [enum Variant.Type]. For example, [code]7[/code] ([int]) and [code]7.0[/code] ([float]) are not considered equal for this method.
396397
</description>
397398
</method>
399+
<method name="find_custom" qualifiers="const">
400+
<return type="int" />
401+
<param index="0" name="method" type="Callable" />
402+
<param index="1" name="from" type="int" default="0" />
403+
<description>
404+
Returns the index of the [b]first[/b] element in the array that causes [param method] to return [code]true[/code], or [code]-1[/code] if there are none. The search's start can be specified with [param from], continuing to the end of the array.
405+
[param method] is a callable that takes an element of the array, and returns a [bool].
406+
[b]Note:[/b] If you just want to know whether the array contains [i]anything[/i] that satisfies [param method], use [method any].
407+
[codeblocks]
408+
[gdscript]
409+
func is_even(number):
410+
return number % 2 == 0
411+
412+
func _ready():
413+
print([1, 3, 4, 7].find_custom(is_even.bind())) # prints 2
414+
[/gdscript]
415+
[/codeblocks]
416+
</description>
417+
</method>
398418
<method name="front" qualifiers="const">
399419
<return type="Variant" />
400420
<description>
@@ -618,6 +638,17 @@
618638
func is_length_greater(a, b):
619639
return a.length() &gt; b.length()
620640
[/codeblock]
641+
This method can also be used to count how many elements in an array satisfy a certain condition, similar to [method count]:
642+
[codeblock]
643+
func is_even(number):
644+
return number % 2 == 0
645+
646+
func _ready():
647+
var arr = [1, 2, 3, 4, 5]
648+
# Increment count if it's even, else leaves count the same.
649+
var even_count = arr.reduce(func(count, next): return count + 1 if is_even(next) else count, 0)
650+
print(even_count) # Prints 2
651+
[/codeblock]
621652
See also [method map], [method filter], [method any] and [method all].
622653
</description>
623654
</method>
@@ -654,6 +685,14 @@
654685
Returns the index of the [b]last[/b] occurrence of [param what] in this array, or [code]-1[/code] if there are none. The search's start can be specified with [param from], continuing to the beginning of the array. This method is the reverse of [method find].
655686
</description>
656687
</method>
688+
<method name="rfind_custom" qualifiers="const">
689+
<return type="int" />
690+
<param index="0" name="method" type="Callable" />
691+
<param index="1" name="from" type="int" default="-1" />
692+
<description>
693+
Returns the index of the [b]last[/b] element of the array that causes [param method] to return [code]true[/code], or [code]-1[/code] if there are none. The search's start can be specified with [param from], continuing to the beginning of the array. This method is the reverse of [method find_custom].
694+
</description>
695+
</method>
657696
<method name="shuffle">
658697
<return type="void" />
659698
<description>

tests/core/variant/test_array.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,24 @@ TEST_CASE("[Array] Typed copying") {
634634
a6.clear();
635635
}
636636

637+
static bool _find_custom_callable(const Variant &p_val) {
638+
return (int)p_val % 2 == 0;
639+
}
640+
641+
TEST_CASE("[Array] Test find_custom") {
642+
Array a1 = build_array(1, 3, 4, 5, 8, 9);
643+
// Find first even number.
644+
int index = a1.find_custom(callable_mp_static(_find_custom_callable));
645+
CHECK_EQ(index, 2);
646+
}
647+
648+
TEST_CASE("[Array] Test rfind_custom") {
649+
Array a1 = build_array(1, 3, 4, 5, 8, 9);
650+
// Find last even number.
651+
int index = a1.rfind_custom(callable_mp_static(_find_custom_callable));
652+
CHECK_EQ(index, 4);
653+
}
654+
637655
} // namespace TestArray
638656

639657
#endif // TEST_ARRAY_H

0 commit comments

Comments
 (0)