Skip to content

Commit

Permalink
The SUM, MIN and MAX functions now also can handle ARRAY parameter ty…
Browse files Browse the repository at this point in the history
…pes. (#449)

This also works recursively for nested arrays and combinations of arrays and numbers.
  • Loading branch information
uklimaschewski authored Mar 19, 2024
1 parent 1d30e3b commit 051d189
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 16 deletions.
6 changes: 3 additions & 3 deletions docs/references/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ Available through the _ExpressionConfiguration.StandardFunctionsDictionary_ cons
| IF(condition, resultIfTrue, resultIfFalse) | Conditional evaluation function. If _condition_ is true, the _resultIfTrue_ is returned, else the _resultIfFalse_ value |
| LOG(value) | The natural logarithm (base e) of a value |
| LOG10(value) | The base 10 logarithm of a value |
| MAX(value, ...) | Returns the maximum value of all parameters |
| MIN(value, ...) | Returns the maximum value of all parameters |
| MAX(value, ...) | Returns the maximum value of all parameters. If a parameter is of type _ARRAY_, the maximum of all elements is used. |
| MIN(value, ...) | Returns the minimum value of all parameters. If a parameter is of type _ARRAY_, the minimum of all elements is used. |
| NOT(value) | Boolean negation, implemented as a function (for compatibility) |
| RANDOM() | Produces a random value between 0 and 1 |
| ROUND(value, scale) | Rounds the given value to the specified scale, using the current rounding mode |
| SQRT(value) | Square root function |
| SUM(value, ...) | Returns the sum of all parameters |
| SUM(value, ...) | Returns the sum of all parameters. If a parameter is of type _ARRAY_, the sum of all elements is calculated. |

### String Functions

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
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.data.EvaluationValue;
import com.ezylang.evalex.functions.AbstractFunction;
import com.ezylang.evalex.functions.FunctionParameter;
import java.math.BigDecimal;

@FunctionParameter(name = "value", isVarArg = true)
public abstract class AbstractMinMaxFunction extends AbstractFunction {
BigDecimal findMinOrMax(BigDecimal current, EvaluationValue parameter, boolean findMin) {
if (parameter.isArrayValue()) {
for (EvaluationValue element : parameter.getArrayValue()) {
current = findMinOrMax(current, element, findMin);
}
} else {
current = compareAndAssign(current, parameter.getNumberValue(), findMin);
}
return current;
}

BigDecimal compareAndAssign(BigDecimal current, BigDecimal newValue, boolean findMin) {
if (current == null
|| (findMin ? newValue.compareTo(current) < 0 : newValue.compareTo(current) > 0)) {
current = newValue;
}
return current;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,20 @@

import com.ezylang.evalex.Expression;
import com.ezylang.evalex.data.EvaluationValue;
import com.ezylang.evalex.functions.AbstractFunction;
import com.ezylang.evalex.functions.FunctionParameter;
import com.ezylang.evalex.parser.Token;
import java.math.BigDecimal;

/** Returns the maximum value of all parameters. */
@FunctionParameter(name = "value", isVarArg = true)
public class MaxFunction extends AbstractFunction {
public class MaxFunction extends AbstractMinMaxFunction {
@Override
public EvaluationValue evaluate(
Expression expression, Token functionToken, EvaluationValue... parameterValues) {
BigDecimal max = null;
BigDecimal min = null;
for (EvaluationValue parameter : parameterValues) {
if (max == null || parameter.getNumberValue().compareTo(max) > 0) {
max = parameter.getNumberValue();
}
min = findMinOrMax(min, parameter, false);
}
return expression.convertValue(max);
return expression.convertValue(min);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,19 @@

import com.ezylang.evalex.Expression;
import com.ezylang.evalex.data.EvaluationValue;
import com.ezylang.evalex.functions.AbstractFunction;
import com.ezylang.evalex.functions.FunctionParameter;
import com.ezylang.evalex.parser.Token;
import java.math.BigDecimal;

/** Returns the minimum value of all parameters. */
@FunctionParameter(name = "value", isVarArg = true)
public class MinFunction extends AbstractFunction {
public class MinFunction extends AbstractMinMaxFunction {
@Override
public EvaluationValue evaluate(
Expression expression, Token functionToken, EvaluationValue... parameterValues) {
BigDecimal min = null;
for (EvaluationValue parameter : parameterValues) {
if (min == null || parameter.getNumberValue().compareTo(min) < 0) {
min = parameter.getNumberValue();
}
min = findMinOrMax(min, parameter, true);
}
return expression.convertValue(min);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,24 @@ public EvaluationValue evaluate(
Expression expression, Token functionToken, EvaluationValue... parameterValues) {
BigDecimal sum = BigDecimal.ZERO;
for (EvaluationValue parameter : parameterValues) {
sum = sum.add(parameter.getNumberValue(), expression.getConfiguration().getMathContext());
sum =
sum.add(
recursiveSum(parameter, expression), expression.getConfiguration().getMathContext());
}
return expression.convertValue(sum);
}

private BigDecimal recursiveSum(EvaluationValue parameter, Expression expression) {
BigDecimal sum = BigDecimal.ZERO;
if (parameter.isArrayValue()) {
for (EvaluationValue element : parameter.getArrayValue()) {
sum =
sum.add(
recursiveSum(element, expression), expression.getConfiguration().getMathContext());
}
} else {
sum = sum.add(parameter.getNumberValue(), expression.getConfiguration().getMathContext());
}
return sum;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
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 static org.assertj.core.api.Assertions.assertThat;

import com.ezylang.evalex.EvaluationException;
import com.ezylang.evalex.Expression;
import com.ezylang.evalex.parser.ParseException;
import org.junit.jupiter.api.Test;

class SumMinMaxArrayTest {

@Test
void testSumSingleArray() throws EvaluationException, ParseException {
Integer[] numbers = {1, 2, 3};

Expression expression = new Expression("SUM(numbers)").with("numbers", numbers);

assertThat(expression.evaluate().getStringValue()).isEqualTo("6");
}

@Test
void testSumMultipleArray() throws EvaluationException, ParseException {
Integer[] numbers1 = {1, 2, 3};
Integer[] numbers2 = {4, 5, 6};

Expression expression =
new Expression("SUM(numbers1, numbers2)")
.with("numbers1", numbers1)
.with("numbers2", numbers2);

assertThat(expression.evaluate().getStringValue()).isEqualTo("21");
}

@Test
void testSumMixedArrayNumber() throws EvaluationException, ParseException {
Integer[] numbers = {1, 2, 3};

Expression expression = new Expression("SUM(numbers, 4)").with("numbers", numbers);

assertThat(expression.evaluate().getStringValue()).isEqualTo("10");
}

@Test
void testSumNestedArray() throws EvaluationException, ParseException {
Integer[][] numbers = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

Expression expression = new Expression("SUM(numbers)").with("numbers", numbers);
assertThat(expression.evaluate().getStringValue()).isEqualTo("45");
}

@Test
void testMinSingleArray() throws EvaluationException, ParseException {
Integer[] numbers = {5, 2, 3};

Expression expression = new Expression("MIN(numbers)").with("numbers", numbers);

assertThat(expression.evaluate().getStringValue()).isEqualTo("2");
}

@Test
void testMinMultipleArray() throws EvaluationException, ParseException {
Integer[] numbers1 = {5, 8, 3};
Integer[] numbers2 = {9, 2, 6};

Expression expression =
new Expression("MIN(numbers1, numbers2)")
.with("numbers1", numbers1)
.with("numbers2", numbers2);

assertThat(expression.evaluate().getStringValue()).isEqualTo("2");
}

@Test
void testMinMixedArrayNumberMinNumber() throws EvaluationException, ParseException {
Integer[] numbers = {1, 2, 3};

Expression expression = new Expression("MIN(numbers, 0)").with("numbers", numbers);

assertThat(expression.evaluate().getStringValue()).isEqualTo("0");
}

@Test
void testMinMixedArrayNumberMinArray() throws EvaluationException, ParseException {
Integer[] numbers = {8, 2, 3};

Expression expression = new Expression("MIN(numbers, 7)").with("numbers", numbers);

assertThat(expression.evaluate().getStringValue()).isEqualTo("2");
}

@Test
void testMinNestedArray() throws EvaluationException, ParseException {
Integer[][] numbers = {{4, 5, 6}, {1, 2, 3}, {7, 8, 9}};

Expression expression = new Expression("MIN(numbers)").with("numbers", numbers);
assertThat(expression.evaluate().getStringValue()).isEqualTo("1");
}

@Test
void testMaxSingleArray() throws EvaluationException, ParseException {
Integer[] numbers = {1, 5, 3};

Expression expression = new Expression("MAX(numbers)").with("numbers", numbers);

assertThat(expression.evaluate().getStringValue()).isEqualTo("5");
}

@Test
void testMaxMultipleArray() throws EvaluationException, ParseException {
Integer[] numbers1 = {5, 2, 3};
Integer[] numbers2 = {4, 9, 6};

Expression expression =
new Expression("MAX(numbers1, numbers2)")
.with("numbers1", numbers1)
.with("numbers2", numbers2);

assertThat(expression.evaluate().getStringValue()).isEqualTo("9");
}

@Test
void testMaxMixedArrayNumberMaxNumber() throws EvaluationException, ParseException {
Integer[] numbers = {1, 2, 3};

Expression expression = new Expression("MAX(numbers, 4)").with("numbers", numbers);

assertThat(expression.evaluate().getStringValue()).isEqualTo("4");
}

@Test
void testMaxMixedArrayNumberMaxArray() throws EvaluationException, ParseException {
Integer[] numbers = {1, 9, 3};

Expression expression = new Expression("MAX(numbers, 4)").with("numbers", numbers);

assertThat(expression.evaluate().getStringValue()).isEqualTo("9");
}

@Test
void testMaxNestedArray() throws EvaluationException, ParseException {
Integer[][] numbers = {{1, 2, 3}, {7, 8, 9}, {4, 5, 6}};

Expression expression = new Expression("MAX(numbers)").with("numbers", numbers);
assertThat(expression.evaluate().getStringValue()).isEqualTo("9");
}
}

0 comments on commit 051d189

Please sign in to comment.