summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Chabora <kobewi4e@gmail.com>2020-10-19 21:21:16 +0200
committerkobewi <kobewi4e@gmail.com>2021-05-05 15:54:57 +0200
commitc50acc7339162eac734b20344c6422b740b978ab (patch)
tree3fe18cdce547c3899c0663810212baf6a6b6cc52
parenteb57dcdb909dd45eaff6e25858a1907e13df4f59 (diff)
Add filter, map and reduce to Array
-rw-r--r--core/variant/array.cpp73
-rw-r--r--core/variant/array.h3
-rw-r--r--core/variant/variant_call.cpp3
-rw-r--r--doc/classes/Array.xml53
4 files changed, 132 insertions, 0 deletions
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 2fb2dd4a30..3c7e2a0719 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -361,6 +361,79 @@ Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const { // l
return new_arr;
}
+Array Array::filter(const Callable &p_callable) const {
+ Array new_arr;
+ new_arr.resize(size());
+ int accepted_count = 0;
+
+ for (int i = 0; i < size(); i++) {
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *));
+ 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(Array(), "Error calling method from 'filter': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
+ }
+
+ if (result.operator bool()) {
+ new_arr[accepted_count] = get(i);
+ accepted_count++;
+ }
+ }
+
+ new_arr.resize(accepted_count);
+
+ return new_arr;
+}
+
+Array Array::map(const Callable &p_callable) const {
+ Array new_arr;
+ new_arr.resize(size());
+
+ for (int i = 0; i < size(); i++) {
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *));
+ 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(Array(), "Error calling method from 'map': " + Variant::get_callable_error_text(p_callable, argptrs, 1, ce));
+ }
+
+ new_arr[i] = result;
+ }
+
+ return new_arr;
+}
+
+Variant Array::reduce(const Callable &p_callable, const Variant &p_accum) const {
+ int start = 0;
+ Variant ret = p_accum;
+ if (ret == Variant() && size() > 0) {
+ ret = front();
+ start = 1;
+ }
+
+ for (int i = start; i < size(); i++) {
+ const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * 2);
+ argptrs[0] = &ret;
+ argptrs[1] = &get(i);
+
+ Variant result;
+ Callable::CallError ce;
+ p_callable.call(argptrs, 2, result, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(Variant(), "Error calling method from 'reduce': " + Variant::get_callable_error_text(p_callable, argptrs, 2, ce));
+ }
+ ret = result;
+ }
+
+ return ret;
+}
+
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 5ce977ee4b..540dcb1f4e 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -101,6 +101,9 @@ public:
Array duplicate(bool p_deep = false) const;
Array slice(int p_begin, int p_end, int p_step = 1, bool p_deep = false) const;
+ 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 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 deaccc6304..8988a0c4ed 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1666,6 +1666,9 @@ static void _register_variant_builtin_methods() {
bind_method(Array, reverse, sarray(), varray());
bind_method(Array, duplicate, sarray("deep"), varray(false));
bind_method(Array, slice, sarray("begin", "end", "step", "deep"), varray(1, false));
+ 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, max, sarray(), varray());
bind_method(Array, min, sarray(), varray());
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 38b74cb436..404528db9a 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -258,6 +258,23 @@
[/codeblocks]
</description>
</method>
+ <method name="filter" qualifiers="const">
+ <return type="Array">
+ </return>
+ <argument index="0" name="method" type="Callable">
+ </argument>
+ <description>
+ Calls the provided [Callable] for each element in array and removes all elements for which the method returned [code]false[/code].
+ [codeblock]
+ func _ready():
+ print([1, 2, 3].filter(remove_1)) # Prints [2, 3].
+ print([1, 2, 3].filter(func(number): return number != 1)) # Same as above, but using lambda function.
+
+ func remove_1(number):
+ return number != 1
+ [/codeblock]
+ </description>
+ </method>
<method name="find" qualifiers="const">
<return type="int">
</return>
@@ -356,6 +373,23 @@
Returns [code]true[/code] if the array is empty.
</description>
</method>
+ <method name="map" qualifiers="const">
+ <return type="Array">
+ </return>
+ <argument index="0" name="method" type="Callable">
+ </argument>
+ <description>
+ Calls the provided [Callable] for each element in array and replaces them with return value of the method.
+ [codeblock]
+ func _ready():
+ print([1, 2, 3].map(negate)) # Prints [-1, -2, -3].
+ print([1, 2, 3].map(func(number): return -number)) # Same as above, but using lambda function.
+
+ func negate(number):
+ return -number
+ [/codeblock]
+ </description>
+ </method>
<method name="max" qualifiers="const">
<return type="Variant">
</return>
@@ -468,6 +502,25 @@
[b]Note:[/b] On large arrays, this method is much slower than [method push_back] as it will reindex all the array's elements every time it's called. The larger the array, the slower [method push_front] will be.
</description>
</method>
+ <method name="reduce" qualifiers="const">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="method" type="Callable">
+ </argument>
+ <argument index="1" name="accum" type="Variant" default="null">
+ </argument>
+ <description>
+ Calls the provided [Callable] for each element in array and accumulates the result in [code]accum[/code]. The method for [Callable] takse two arguments: current value of [code]accum[/code] and the current array element. If [code]accum[/code] is [code]null[/code] (default value), the method will use first element from the array as initial value.
+ [codeblock]
+ func _ready():
+ print([1, 2, 3].reduce(factorial, 1)) # Prints 6.
+ print([1, 2, 3].reduce(func(accum, number): return accum * number)) # Same as above, but using lambda function.
+
+ func factorial(accum, number):
+ return accum * number
+ [/codeblock]
+ </description>
+ </method>
<method name="remove">
<return type="void">
</return>