From 4c9aa3ac14c6ae23412c859e9eeca92b248c96ad Mon Sep 17 00:00:00 2001 From: Philip Hell Date: Thu, 23 Jan 2025 15:21:20 +0100 Subject: [PATCH] implemented the standard-function called filter --- README-X.md | 18 +++- README.md | 66 +++++++++++++- src/main/java/me/blvckbytes/gpeee/GPEEE.java | 1 + .../gpeee/functions/std/FilterFunction.java | 85 +++++++++++++++++++ .../gpeee/std/FilterFunctionTests.java | 57 +++++++++++++ 5 files changed, 222 insertions(+), 5 deletions(-) create mode 100644 src/main/java/me/blvckbytes/gpeee/functions/std/FilterFunction.java create mode 100644 src/test/java/me/blvckbytes/gpeee/std/FilterFunctionTests.java diff --git a/README-X.md b/README-X.md index 600b89c..f3f3ad4 100644 --- a/README-X.md +++ b/README-X.md @@ -412,7 +412,7 @@ list_of(value...?: Object): List ### map -Iterate over a collection while mapping each iteration through a lambda function, who's result +Iterate over a collection while mapping each iteration through a lambda function, whose result is being appended to the final result list. | Argument | Description | @@ -427,6 +427,22 @@ map(items: Collection, mapper: (item: Object, index: Number) => String, fallb +### filter + +Iterate over a collection while mapping each item through a lambda function, whose result +is being interpreted as a filter predicate. + +| Argument | Description | +|-----------|--------------------------------------| +| items | Collection to iterate | +| mapper | Lambda function to filter items with | + +``` +filter(items: Collection, mapper: (item: Object, index: Number) => String): List +``` + + + ### map_of Create a list from a variable amount of scalar input value pairs. diff --git a/README.md b/README.md index ea1aeb9..48c291c 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ want to integrate into your next project. - [list](#list) - [list_of](#list_of) - [map](#map) + - [filter](#filter) - [map_of](#map_of) - [print](#print) - [r_index](#r_index) @@ -136,6 +137,11 @@ public interface IExpressionEvaluator { */ Object evaluateExpression(AExpression expression, IEvaluationEnvironment environment) throws AEvaluatorError; + /** + * Get a copy of the evaluator's base environment to be safely modified and built by the caller + */ + EvaluationEnvironmentBuilder getBaseEnvironment(); + } ``` @@ -269,9 +275,9 @@ public class EvaluationEnvironmentBuilder { } public IEvaluationEnvironment build(@Nullable IEvaluationEnvironment environmentToExtend) { - Map resultingFunctions = new HashMap<>(this.functions); - Map> resultingLiveVariables = new HashMap<>(this.liveVariables); - Map resultingStaticVariables = new HashMap<>(this.staticVariables); + Map resultingFunctions = new HashMap<>(); + Map> resultingLiveVariables = new HashMap<>(); + Map resultingStaticVariables = new HashMap<>(); if (environmentToExtend != null) { resultingFunctions.putAll(environmentToExtend.getFunctions()); @@ -279,6 +285,11 @@ public class EvaluationEnvironmentBuilder { resultingStaticVariables.putAll(environmentToExtend.getStaticVariables()); } + // Put builder-items last, as to make them prevail over the possibly extended environment + resultingFunctions.putAll(this.functions); + resultingLiveVariables.putAll(this.liveVariables); + resultingStaticVariables.putAll(this.staticVariables); + return new IEvaluationEnvironment() { @Override @@ -1470,7 +1481,7 @@ public class ListOfFunctionTests { ### map -Iterate over a collection while mapping each iteration through a lambda function, who's result +Iterate over a collection while mapping each iteration through a lambda function, whose result is being appended to the final result list. | Argument | Description | @@ -1527,6 +1538,53 @@ public class MapFunctionTests { +### filter + +Iterate over a collection while mapping each item through a lambda function, whose result +is being interpreted as a filter predicate. + +| Argument | Description | +|-----------|--------------------------------------| +| items | Collection to iterate | +| mapper | Lambda function to filter items with | + +``` +filter(items: Collection, mapper: (item: Object, index: Number) => String): List +``` + +
+FilterFunctionTests.java + +```java +package me.blvckbytes.gpeee.std; + +public class FilterFunctionTests { + + @Test + public void shouldRequireArguments() { + new EnvironmentBuilder() + .withStaticVariable("items", Collections.emptyList()) + .launch(validator -> { + validator.validateThrows("filter()", InvalidFunctionArgumentTypeError.class); + validator.validateThrows("filter(items)", InvalidFunctionArgumentTypeError.class); + }); + } + + @Test + public void shouldFilterInputItems() { + new EnvironmentBuilder() + .withStaticVariable("items", Arrays.asList("a", "b", "c", null)) + .launch(validator -> { + validator.validate("filter(items, (item) => item != \"a\")", Arrays.asList("b", "c", null)); + validator.validate("filter(items, (item) => item != \"c\")", Arrays.asList("a", "b", null)); + validator.validate("filter(items, (item) => item != null)", Arrays.asList("a", "b", "c")); + }); + } +} +``` +
+ + ### map_of Create a list from a variable amount of scalar input value pairs. diff --git a/src/main/java/me/blvckbytes/gpeee/GPEEE.java b/src/main/java/me/blvckbytes/gpeee/GPEEE.java index 5124373..9b66e50 100644 --- a/src/main/java/me/blvckbytes/gpeee/GPEEE.java +++ b/src/main/java/me/blvckbytes/gpeee/GPEEE.java @@ -125,6 +125,7 @@ private void loadStandardFunctions() { new PrintFunction().registerSelf(this); new TitleCaseFunction().registerSelf(this); new MapFunction().registerSelf(this); + new FilterFunction().registerSelf(this); new DateFormatFunction().registerSelf(this); new LIndexFunction().registerSelf(this); new RIndexFunction().registerSelf(this); diff --git a/src/main/java/me/blvckbytes/gpeee/functions/std/FilterFunction.java b/src/main/java/me/blvckbytes/gpeee/functions/std/FilterFunction.java new file mode 100644 index 0000000..ec1bd0b --- /dev/null +++ b/src/main/java/me/blvckbytes/gpeee/functions/std/FilterFunction.java @@ -0,0 +1,85 @@ +/* + * MIT License + * + * Copyright (c) 2025 BlvckBytes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.blvckbytes.gpeee.functions.std; + +import me.blvckbytes.gpeee.functions.AExpressionFunction; +import me.blvckbytes.gpeee.functions.ExpressionFunctionArgument; +import me.blvckbytes.gpeee.functions.IStandardFunctionRegistry; +import me.blvckbytes.gpeee.interpreter.IEvaluationEnvironment; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Filter collections - filter + * + * Filters a collection of items by running each through a callback expression + * which will map it to a boolean value and then collects passing items in the resulting collection. + */ +public class FilterFunction extends AStandardFunction { + + @Override + public Object apply(IEvaluationEnvironment env, List<@Nullable Object> args) { + // Retrieve arguments + Iterable items = nonNull(args, 0); + AExpressionFunction mapper = nonNull(args, 1); + + List result = new ArrayList<>(); + + // Loop all items with their indices + int c = 0; + for (Object item : items) { + Object mapperResult = mapper.apply(env, Arrays.asList(item, c++)); + + if (!env.getValueInterpreter().asBoolean(mapperResult)) + continue; + + result.add(item); + } + + return result; + } + + @Override + public @Nullable List getArguments() { + // filter(items, (it, ind) => (..)) + return Arrays.asList( + new ExpressionFunctionArgument("items", "Items to iterate", true, Iterable.class), + new ExpressionFunctionArgument("mapper", "Iteration item mapper function", true, AExpressionFunction.class) + ); + } + + @Override + public void registerSelf(IStandardFunctionRegistry registry) { + registry.register("filter", this); + } + + @Override + public boolean returnsPrimaryResult() { + return true; + } +} diff --git a/src/test/java/me/blvckbytes/gpeee/std/FilterFunctionTests.java b/src/test/java/me/blvckbytes/gpeee/std/FilterFunctionTests.java new file mode 100644 index 0000000..1c3effe --- /dev/null +++ b/src/test/java/me/blvckbytes/gpeee/std/FilterFunctionTests.java @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2025 BlvckBytes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.blvckbytes.gpeee.std; + +import me.blvckbytes.gpeee.EnvironmentBuilder; +import me.blvckbytes.gpeee.error.InvalidFunctionArgumentTypeError; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +public class FilterFunctionTests { + + @Test + public void shouldRequireArguments() { + new EnvironmentBuilder() + .withStaticVariable("items", Collections.emptyList()) + .launch(validator -> { + validator.validateThrows("filter()", InvalidFunctionArgumentTypeError.class); + validator.validateThrows("filter(items)", InvalidFunctionArgumentTypeError.class); + }); + } + + @Test + public void shouldFilterInputItems() { + new EnvironmentBuilder() + .withStaticVariable("items", Arrays.asList("a", "b", "c", null)) + .launch(validator -> { + validator.validate("filter(items, (item) => item != \"a\")", Arrays.asList("b", "c", null)); + validator.validate("filter(items, (item) => item != \"c\")", Arrays.asList("a", "b", null)); + validator.validate("filter(items, (item) => item != null)", Arrays.asList("a", "b", "c")); + }); + } +}