Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filter, map and reduce to Array #38645

Merged
merged 1 commit into from
May 6, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions core/variant/array.cpp
Original file line number Diff line number Diff line change
@@ -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;
3 changes: 3 additions & 0 deletions core/variant/array.h
Original file line number Diff line number Diff line change
@@ -101,6 +101,9 @@ class Array {
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;
3 changes: 3 additions & 0 deletions core/variant/variant_call.cpp
Original file line number Diff line number Diff line change
@@ -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());

53 changes: 53 additions & 0 deletions doc/classes/Array.xml
Original file line number Diff line number Diff line change
@@ -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>