gdbasics/scripts/libs/algorithm.gd
2021-08-21 17:13:19 +02:00

119 lines
3.7 KiB
GDScript

###
### algorithmic utility
###
extends Object
class_name GDBAlgorithm
class __SortBy:
var prop_name := ""
var descending := false
func __sort(obj0, obj1):
var res = obj0.get(prop_name) < obj1.get(prop_name)
if descending:
return !res
return res
################
# public stuff #
################
func _init() -> void:
assert(0, "This class should not be instantiated.")
# Packs source code into a small script object, like a lambda in other languages.
# This function can then be called by calling the "eval" (or whatever you pass as
# fn_name) function of the object. This is especially useful for GDScript functions
# like "sort_custom" which take an object and a function as parameters.
#
# Example:
# var names := ["Bob", "Alice", "Elfriede"]
# names.sort_custom(GDBAlgorithm.lambda("return name0.substr(1) < name1.substr(1)", ["name0", "name1"]), "eval")
# print(names) # prints "[Elfriede, Alice, Bob]"
#
# @param source The source code of your lambda function.
# @param params A list of parameter names your function takes.
# @param fn_name The name of the resulting function, defaults to "eval".
# @returns An object containing the lambda function as fn_name.
static func lambda(source : String, params := [], fn_name := "eval") -> Reference:
var script := GDScript.new();
script.source_code = "tool\nextends Reference\nfunc {fn_name}({param_list}):\n\t{source}".format({
"source": source,
"param_list": PoolStringArray(params).join(","),
"fn_name": fn_name
})
var err : int = script.reload()
if err == OK:
return script.new()
else:
printerr("lambda: script compilation failed.")
return null
# Removes elements from a collection only if the specified condition is met.
# Takes either an object and a function name or a lambda and a parameter list
# as parameters.
#
# Example 1:
# func is_negative(num):
# return num < 0
#
# func remove_negatives(col):
# GDBAlgorithm.remove_if(col, self, "is_negative")
#
# Example 2:
# func remove_negatives2(col):
# GDBAlgorithm.remove_if(col, "num < 0", ["num"])
static func remove_if(collection, p0, p1 = null) -> int:
if p0 is String:
if p1 && !p1 is Array:
printerr("remove_if failed: invalid parameters")
return 0
var lmb := lambda(p0 as String, p1 as Array if p1 else [])
if !lmb:
return 0 # errors have already been printed
return __remove_if(collection, lmb, "eval")
elif !p0 is Object || !p1 is String:
printerr("remove_if failed: invalid parameters")
return 0
else:
return __remove_if(collection, p0, p1)
static func sort_by(arr : Array, prop_name : String, descending := false):
var comparator := __SortBy.new()
comparator.prop_name = prop_name
comparator.descending = descending
arr.sort_custom(comparator, "__sort")
#################
# private stuff #
#################
static func __remove_if(collection, object : Object, predicate : String) -> int:
if collection is Array:
return __remove_if_array(collection, object, predicate)
elif collection is Dictionary:
return __remove_if_dict(collection, object, predicate)
else:
var values_to_remove := []
for ele in collection:
if object.call(predicate, ele):
values_to_remove.append(ele)
for ele in values_to_remove:
collection.erase(ele)
return values_to_remove.size()
static func __remove_if_array(array : Array, object : Object, predicate : String) -> int:
var removed := 0
for i in range(array.size() - 1, -1, -1):
if object.call(predicate, array[i]):
array.remove(i)
removed += 1
return removed
static func __remove_if_dict(dict : Dictionary, object : Object, predicate : String) -> int:
var removed := 0
for key in dict.keys():
if object.call(predicate, dict[key]):
dict.erase(key)
removed += 1
return removed