Merge pull request #95449 from SlashScreen/array_functions

Add callable support for `find` and `rfind` `Array` methods
This commit is contained in:
Rémi Verschelde 2024-09-20 16:06:13 +02:00
commit 6bf8a3e3f8
No known key found for this signature in database
GPG Key ID: C3336907360768E1
5 changed files with 125 additions and 1 deletions

View File

@ -369,6 +369,34 @@ int Array::find(const Variant &p_value, int p_from) const {
return ret; return ret;
} }
int Array::find_custom(const Callable &p_callable, int p_from) const {
int ret = -1;
if (p_from < 0 || size() == 0) {
return ret;
}
const Variant *argptrs[1];
for (int i = p_from; i < size(); i++) {
const Variant &val = _p->array[i];
argptrs[0] = &val;
Variant res;
Callable::CallError ce;
p_callable.callp(argptrs, 1, res, ce);
if (unlikely(ce.error != Callable::CallError::CALL_OK)) {
ERR_FAIL_V_MSG(ret, "Error calling method from 'find_custom': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
}
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.");
if (res.operator bool()) {
return i;
}
}
return ret;
}
int Array::rfind(const Variant &p_value, int p_from) const { int Array::rfind(const Variant &p_value, int p_from) const {
if (_p->array.size() == 0) { if (_p->array.size() == 0) {
return -1; return -1;
@ -394,6 +422,41 @@ int Array::rfind(const Variant &p_value, int p_from) const {
return -1; return -1;
} }
int Array::rfind_custom(const Callable &p_callable, int p_from) const {
if (_p->array.size() == 0) {
return -1;
}
if (p_from < 0) {
// Relative offset from the end.
p_from = _p->array.size() + p_from;
}
if (p_from < 0 || p_from >= _p->array.size()) {
// Limit to array boundaries.
p_from = _p->array.size() - 1;
}
const Variant *argptrs[1];
for (int i = p_from; i >= 0; i--) {
const Variant &val = _p->array[i];
argptrs[0] = &val;
Variant res;
Callable::CallError ce;
p_callable.callp(argptrs, 1, res, ce);
if (unlikely(ce.error != Callable::CallError::CALL_OK)) {
ERR_FAIL_V_MSG(-1, "Error calling method from 'rfind_custom': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
}
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.");
if (res.operator bool()) {
return i;
}
}
return -1;
}
int Array::count(const Variant &p_value) const { int Array::count(const Variant &p_value) const {
Variant value = p_value; Variant value = p_value;
ERR_FAIL_COND_V(!_p->typed.validate(value, "count"), 0); ERR_FAIL_COND_V(!_p->typed.validate(value, "count"), 0);
@ -761,7 +824,7 @@ Variant Array::max() const {
return Variant(); //not a valid comparison return Variant(); //not a valid comparison
} }
if (bool(ret)) { if (bool(ret)) {
//is less //is greater
maxval = test; maxval = test;
} }
} }

View File

@ -152,7 +152,9 @@ public:
void reverse(); void reverse();
int find(const Variant &p_value, int p_from = 0) const; int find(const Variant &p_value, int p_from = 0) const;
int find_custom(const Callable &p_callable, int p_from = 0) const;
int rfind(const Variant &p_value, int p_from = -1) const; int rfind(const Variant &p_value, int p_from = -1) const;
int rfind_custom(const Callable &p_callable, int p_from = -1) const;
int count(const Variant &p_value) const; int count(const Variant &p_value) const;
bool has(const Variant &p_value) const; bool has(const Variant &p_value) const;

View File

@ -2305,7 +2305,9 @@ static void _register_variant_builtin_methods_array() {
bind_method(Array, back, sarray(), varray()); bind_method(Array, back, sarray(), varray());
bind_method(Array, pick_random, sarray(), varray()); bind_method(Array, pick_random, sarray(), varray());
bind_method(Array, find, sarray("what", "from"), varray(0)); bind_method(Array, find, sarray("what", "from"), varray(0));
bind_method(Array, find_custom, sarray("method", "from"), varray(0));
bind_method(Array, rfind, sarray("what", "from"), varray(-1)); bind_method(Array, rfind, sarray("what", "from"), varray(-1));
bind_method(Array, rfind_custom, sarray("method", "from"), varray(-1));
bind_method(Array, count, sarray("value"), varray()); bind_method(Array, count, sarray("value"), varray());
bind_method(Array, has, sarray("value"), varray()); bind_method(Array, has, sarray("value"), varray());
bind_method(Array, pop_back, sarray(), varray()); bind_method(Array, pop_back, sarray(), varray());

View File

@ -324,6 +324,7 @@
<param index="0" name="value" type="Variant" /> <param index="0" name="value" type="Variant" />
<description> <description>
Returns the number of times an element is in the array. Returns the number of times an element is in the array.
To count how many elements in an array satisfy a condition, see [method reduce].
</description> </description>
</method> </method>
<method name="duplicate" qualifiers="const"> <method name="duplicate" qualifiers="const">
@ -395,6 +396,25 @@
[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. [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.
</description> </description>
</method> </method>
<method name="find_custom" qualifiers="const">
<return type="int" />
<param index="0" name="method" type="Callable" />
<param index="1" name="from" type="int" default="0" />
<description>
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.
[param method] is a callable that takes an element of the array, and returns a [bool].
[b]Note:[/b] If you just want to know whether the array contains [i]anything[/i] that satisfies [param method], use [method any].
[codeblocks]
[gdscript]
func is_even(number):
return number % 2 == 0
func _ready():
print([1, 3, 4, 7].find_custom(is_even.bind())) # prints 2
[/gdscript]
[/codeblocks]
</description>
</method>
<method name="front" qualifiers="const"> <method name="front" qualifiers="const">
<return type="Variant" /> <return type="Variant" />
<description> <description>
@ -618,6 +638,17 @@
func is_length_greater(a, b): func is_length_greater(a, b):
return a.length() &gt; b.length() return a.length() &gt; b.length()
[/codeblock] [/codeblock]
This method can also be used to count how many elements in an array satisfy a certain condition, similar to [method count]:
[codeblock]
func is_even(number):
return number % 2 == 0
func _ready():
var arr = [1, 2, 3, 4, 5]
# Increment count if it's even, else leaves count the same.
var even_count = arr.reduce(func(count, next): return count + 1 if is_even(next) else count, 0)
print(even_count) # Prints 2
[/codeblock]
See also [method map], [method filter], [method any] and [method all]. See also [method map], [method filter], [method any] and [method all].
</description> </description>
</method> </method>
@ -654,6 +685,14 @@
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]. 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].
</description> </description>
</method> </method>
<method name="rfind_custom" qualifiers="const">
<return type="int" />
<param index="0" name="method" type="Callable" />
<param index="1" name="from" type="int" default="-1" />
<description>
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].
</description>
</method>
<method name="shuffle"> <method name="shuffle">
<return type="void" /> <return type="void" />
<description> <description>

View File

@ -634,6 +634,24 @@ TEST_CASE("[Array] Typed copying") {
a6.clear(); a6.clear();
} }
static bool _find_custom_callable(const Variant &p_val) {
return (int)p_val % 2 == 0;
}
TEST_CASE("[Array] Test find_custom") {
Array a1 = build_array(1, 3, 4, 5, 8, 9);
// Find first even number.
int index = a1.find_custom(callable_mp_static(_find_custom_callable));
CHECK_EQ(index, 2);
}
TEST_CASE("[Array] Test rfind_custom") {
Array a1 = build_array(1, 3, 4, 5, 8, 9);
// Find last even number.
int index = a1.rfind_custom(callable_mp_static(_find_custom_callable));
CHECK_EQ(index, 4);
}
} // namespace TestArray } // namespace TestArray
#endif // TEST_ARRAY_H #endif // TEST_ARRAY_H