Browse Source

Merge pull request #38645 from KoBeWi/FMR

Add filter, map and reduce to Array
Rémi Verschelde 4 years ago
parent
commit
01f80201bf
4 changed files with 132 additions and 0 deletions
  1. 73 0
      core/variant/array.cpp
  2. 3 0
      core/variant/array.h
  3. 3 0
      core/variant/variant_call.cpp
  4. 53 0
      doc/classes/Array.xml

+ 73 - 0
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;
 	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 {
 struct _ArrayVariantSort {
 	_FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const {
 	_FORCE_INLINE_ bool operator()(const Variant &p_l, const Variant &p_r) const {
 		bool valid = false;
 		bool valid = false;

+ 3 - 0
core/variant/array.h

@@ -101,6 +101,9 @@ public:
 	Array duplicate(bool p_deep = false) const;
 	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 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;
 	bool operator<=(const Array &p_array) const;
 	bool operator<=(const Array &p_array) const;

+ 3 - 0
core/variant/variant_call.cpp

@@ -1745,6 +1745,9 @@ static void _register_variant_builtin_methods() {
 	bind_method(Array, reverse, sarray(), varray());
 	bind_method(Array, reverse, sarray(), varray());
 	bind_method(Array, duplicate, sarray("deep"), varray(false));
 	bind_method(Array, duplicate, sarray("deep"), varray(false));
 	bind_method(Array, slice, sarray("begin", "end", "step", "deep"), varray(1, 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, max, sarray(), varray());
 	bind_method(Array, min, sarray(), varray());
 	bind_method(Array, min, sarray(), varray());
 
 

+ 53 - 0
doc/classes/Array.xml

@@ -258,6 +258,23 @@
 				[/codeblocks]
 				[/codeblocks]
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="find" qualifiers="const">
 			<return type="int">
 			<return type="int">
 			</return>
 			</return>
@@ -356,6 +373,23 @@
 				Returns [code]true[/code] if the array is empty.
 				Returns [code]true[/code] if the array is empty.
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="max" qualifiers="const">
 			<return type="Variant">
 			<return type="Variant">
 			</return>
 			</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.
 				[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>
 			</description>
 		</method>
 		</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">
 		<method name="remove">
 			<return type="void">
 			<return type="void">
 			</return>
 			</return>