From f37e6515875056ac16f890e99657ec6d22f1741d Mon Sep 17 00:00:00 2001 From: Oswaldo Baptista Vicente Junior <45291656+oswaldobapvicjr@users.noreply.github.com> Date: Sat, 27 Apr 2024 05:18:07 -0300 Subject: [PATCH] Adds new function AVERAGE (#468) --- docs/references/functions.md | 1 + .../config/ExpressionConfiguration.java | 1 + .../functions/basic/AverageFunction.java | 46 +++++++++++++++++++ .../functions/basic/BasicFunctionsTest.java | 26 +++++++++++ 4 files changed, 74 insertions(+) create mode 100644 src/main/java/com/ezylang/evalex/functions/basic/AverageFunction.java diff --git a/docs/references/functions.md b/docs/references/functions.md index ec66880a..f39f2d79 100644 --- a/docs/references/functions.md +++ b/docs/references/functions.md @@ -14,6 +14,7 @@ Available through the _ExpressionConfiguration.StandardFunctionsDictionary_ cons | Name | Description | |--------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| | ABS(value) | Absolute (non-negative) value | +| AVERAGE(value, ...) | Returns the average (arithmetic mean) of all parameters. | | CEILING(value) | Rounds the given value an integer using the rounding mode CEILING | | COALESCE(value, ...) | Returns the first non-null parameter, or NULL if all parameters are null | | FACT(base) | Calculates the factorial of a base value | diff --git a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java index d23ae753..d19522ef 100644 --- a/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java +++ b/src/main/java/com/ezylang/evalex/config/ExpressionConfiguration.java @@ -129,6 +129,7 @@ public class ExpressionConfiguration { MapBasedFunctionDictionary.ofFunctions( // basic functions Map.entry("ABS", new AbsFunction()), + Map.entry("AVERAGE", new AverageFunction()), Map.entry("CEILING", new CeilingFunction()), Map.entry("COALESCE", new CoalesceFunction()), Map.entry("FACT", new FactFunction()), diff --git a/src/main/java/com/ezylang/evalex/functions/basic/AverageFunction.java b/src/main/java/com/ezylang/evalex/functions/basic/AverageFunction.java new file mode 100644 index 00000000..06e15e6d --- /dev/null +++ b/src/main/java/com/ezylang/evalex/functions/basic/AverageFunction.java @@ -0,0 +1,46 @@ +/* + Copyright 2012-2024 Udo Klimaschewski + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.ezylang.evalex.functions.basic; + +import com.ezylang.evalex.Expression; +import com.ezylang.evalex.data.EvaluationValue; +import com.ezylang.evalex.functions.FunctionParameter; +import com.ezylang.evalex.parser.Token; +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.Arrays; + +/** + * Returns the average (arithmetic mean) of the numeric arguments. + * + * @author oswaldo.bapvic.jr + */ +@FunctionParameter(name = "firstValue") +@FunctionParameter(name = "additionalValues", isVarArg = true) +public class AverageFunction extends AbstractMinMaxFunction { + @Override + public EvaluationValue evaluate( + Expression expression, Token functionToken, EvaluationValue... parameterValues) { + MathContext mathContext = expression.getConfiguration().getMathContext(); + BigDecimal sum = + Arrays.stream(parameterValues) + .map(EvaluationValue::getNumberValue) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal count = BigDecimal.valueOf(parameterValues.length); + BigDecimal average = sum.divide(count, mathContext); + return expression.convertValue(average); + } +} diff --git a/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java b/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java index f3a7ddc8..9a55cc45 100644 --- a/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java +++ b/src/test/java/com/ezylang/evalex/functions/basic/BasicFunctionsTest.java @@ -362,4 +362,30 @@ void testCoalesce(String expression, String expectedResult) assertThat(evaluationValue.getStringValue()).isEqualTo(expectedResult); } } + + @ParameterizedTest + @CsvSource( + delimiter = ':', + value = { + "AVERAGE(99) : 99", + "AVERAGE(1,2) : 1.5", + "AVERAGE(1,3) : 2", + "AVERAGE(1.999,2) : 1.9995", + "AVERAGE(-5,0,5) : 0", + "AVERAGE(10,15,32) : 19", + "AVERAGE(7,9,27,2) : 11.25", + "AVERAGE(7,9,27,2,5) : 10", + "AVERAGE(-7,-9,-27,-2,-5) : -10" + }) + void testAverage(String expression, String expectedResult) + throws EvaluationException, ParseException { + assertExpressionHasExpectedResult(expression, expectedResult); + } + + @Test + void testAverageThrowsException() { + assertThatThrownBy(() -> new Expression("AVERAGE()").evaluate()) + .isInstanceOf(ParseException.class) + .hasMessage("Not enough parameters for function"); + } }