From a98e31aa3a78e9b9521407f04971eb9e066177ad Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sat, 10 Jul 2021 20:19:47 +0200 Subject: [PATCH] Add `any()` and `all()` methods to Array These can be used as faster, more convenient shorthands to using `filter()` + `size()`. --- core/variant/array.cpp | 44 ++++++++++++++++++++++++++++++++++ core/variant/array.h | 2 ++ core/variant/variant_call.cpp | 2 ++ doc/classes/Array.xml | 45 +++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 7551350c95a9..b1e142d23957 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -501,6 +501,50 @@ Variant Array::reduce(const Callable &p_callable, const Variant &p_accum) const return ret; } +bool Array::any(const Callable &p_callable) const { + const Variant *argptrs[1]; + for (int i = 0; i < size(); i++) { + argptrs[0] = &get(i); + + Variant result; + Callable::CallError ce; + p_callable.call(argptrs, 1, result, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_V_MSG(false, "Error calling method from 'any': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); + } + + if (result.operator bool()) { + // Return as early as possible when one of the conditions is `true`. + // This improves performance compared to relying on `filter(...).size() >= 1`. + return true; + } + } + + return false; +} + +bool Array::all(const Callable &p_callable) const { + const Variant *argptrs[1]; + for (int i = 0; i < size(); i++) { + argptrs[0] = &get(i); + + Variant result; + Callable::CallError ce; + p_callable.call(argptrs, 1, result, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_FAIL_V_MSG(false, "Error calling method from 'all': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce)); + } + + if (!(result.operator bool())) { + // Return as early as possible when one of the inverted conditions is `false`. + // This improves performance compared to relying on `filter(...).size() >= array_size().`. + return false; + } + } + + return true; +} + struct _ArrayVariantSort { _FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const { bool valid = false; diff --git a/core/variant/array.h b/core/variant/array.h index f537700f995e..c0073767346e 100644 --- a/core/variant/array.h +++ b/core/variant/array.h @@ -108,6 +108,8 @@ class Array { Array filter(const Callable &p_callable) const; Array map(const Callable &p_callable) const; Variant reduce(const Callable &p_callable, const Variant &p_accum) const; + bool any(const Callable &p_callable) const; + bool all(const Callable &p_callable) const; bool operator<(const Array &p_array) const; bool operator<=(const Array &p_array) const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index a3568a8d6a64..d86bc70d2154 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1853,6 +1853,8 @@ static void _register_variant_builtin_methods() { bind_method(Array, filter, sarray("method"), varray()); bind_method(Array, map, sarray("method"), varray()); bind_method(Array, reduce, sarray("method", "accum"), varray(Variant())); + bind_method(Array, any, sarray("method"), varray()); + bind_method(Array, all, sarray("method"), varray()); bind_method(Array, max, sarray(), varray()); bind_method(Array, min, sarray(), varray()); diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index ef4f86f1a9a5..94181db95f2e 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -123,6 +123,48 @@ + + + + + Calls the provided [Callable] on each element in the array and returns [code]true[/code] if the [Callable] returns [code]true[/code] for [i]all[/i] elements in the array. If the [Callable] returns [code]false[/code] for one array element or more, this method returns [code]false[/code]. + The callable's method should take one [Variant] parameter (the current array element) and return a boolean value. + [codeblock] + func _ready(): + print([6, 10, 6].all(greater_than_5)) # Prints True (3 elements evaluate to `true`). + print([4, 10, 4].all(greater_than_5)) # Prints False (1 elements evaluate to `true`). + print([4, 4, 4].all(greater_than_5)) # Prints False (0 elements evaluate to `true`). + + print([6, 10, 6].all(func(number): return number > 5)) # Prints True. Same as the first line above, but using lambda function. + + func greater_than_5(number): + return number > 5 + [/codeblock] + See also [method any], [method filter], [method map] and [method reduce]. + [b]Note:[/b] Unlike relying on the size of an array returned by [method filter], this method will return as early as possible to improve performance (especially with large arrays). + + + + + + + Calls the provided [Callable] on each element in the array and returns [code]true[/code] if the [Callable] returns [code]true[/code] for [i]one or more[/i] elements in the array. If the [Callable] returns [code]false[/code] for all elements in the array, this method returns [code]false[/code]. + The callable's method should take one [Variant] parameter (the current array element) and return a boolean value. + [codeblock] + func _ready(): + print([6, 10, 6].any(greater_than_5)) # Prints True (3 elements evaluate to `true`). + print([4, 10, 4].any(greater_than_5)) # Prints True (1 elements evaluate to `true`). + print([4, 4, 4].any(greater_than_5)) # Prints False (0 elements evaluate to `true`). + + print([6, 10, 6].any(func(number): return number > 5)) # Prints True. Same as the first line above, but using lambda function. + + func greater_than_5(number): + return number > 5 + [/codeblock] + See also [method all], [method filter], [method map] and [method reduce]. + [b]Note:[/b] Unlike relying on the size of an array returned by [method filter], this method will return as early as possible to improve performance (especially with large arrays). + + @@ -232,6 +274,7 @@ func remove_1(number): return number != 1 [/codeblock] + See also [method any], [method all], [method map] and [method reduce]. @@ -333,6 +376,7 @@ func negate(number): return -number [/codeblock] + See also [method filter], [method reduce], [method any] and [method all]. @@ -398,6 +442,7 @@ func sum(accum, number): return accum + number [/codeblock] + See also [method map], [method filter], [method any] and [method all].