Skip to content

Commit e98440a

Browse files
Merge pull request #469 from awensaunders/avg-decimals
Decimals (float) in avg expressions.
2 parents dc5a77f + 4034b9a commit e98440a

File tree

6 files changed

+33
-10
lines changed

6 files changed

+33
-10
lines changed

app/javascript/libs/expressions/operators/avg.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import { MATHEMATICAL_OPERATORS } from "../constants";
77

88
import sumOperator from "./sum";
99

10-
export default expressions => ({
10+
export default (expressions, extra) => ({
1111
expressions,
1212
operator: MATHEMATICAL_OPERATORS.AVG,
1313
evaluate: data => {
14+
const decimalPlaces = extra?.decimalPlaces;
1415
const sum = sumOperator(expressions).evaluate(data);
15-
1616
const count = Object.values(expressions).reduce((prev, current) => {
1717
if (has(data, current) && !isNil(data[current]) && data[current] !== "") {
1818
return prev + 1;
@@ -21,6 +21,13 @@ export default expressions => ({
2121
return prev;
2222
}, 0);
2323

24-
return Math.floor(sum / (count === 0 ? 1 : count));
24+
const res = sum / (count === 0 ? 1 : count);
25+
26+
if (decimalPlaces) {
27+
return parseFloat(res.toFixed(decimalPlaces));
28+
}
29+
30+
// Backwards compatible version rounds down
31+
return Math.floor(res);
2532
}
2633
});

app/javascript/libs/expressions/operators/avg.unit.test.js

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import avgOperator from "./avg";
66

77
describe("avgOperator", () => {
88
const operator = avgOperator(["a", "b", "c"]);
9+
const decimalPlaceOperator = avgOperator(["a", "b", "c"], { decimalPlaces: 3 });
910

1011
it("should return avg", () => {
1112
expect(operator.evaluate({ a: 3, b: 4, c: 2 })).to.deep.equals(3);
@@ -22,4 +23,7 @@ describe("avgOperator", () => {
2223
it("returns 0 when no argument passed", () => {
2324
expect(operator.evaluate({})).to.deep.equals(0);
2425
});
26+
it("returns a float when decimal places are specified", () => {
27+
expect(decimalPlaceOperator.evaluate({ a: 1, b: 4 })).to.deep.equals(2.5);
28+
});
2529
});

app/javascript/libs/expressions/parse-expression.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ const parseExpression = expression => {
1717
}
1818

1919
if (isMathematicalOperator(operator)) {
20-
const mathExp = Array.isArray(value)
21-
? value.map(nested => (isObject(nested) ? parseExpression(nested) : nested))
22-
: parseExpression(value);
20+
const data = value?.data ?? value;
21+
const extra = value?.extra;
22+
const mathExp = Array.isArray(data)
23+
? data.map(nested => (isObject(nested) ? parseExpression(nested) : nested))
24+
: parseExpression(data);
2325

24-
return buildOperator(operator, mathExp);
26+
return buildOperator(operator, mathExp, extra);
2527
}
2628

2729
return buildOperator(operator, value);

app/javascript/libs/expressions/parse-expression.unit.test.js

+10
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ describe("parseExpression", () => {
199199

200200
context("avgOperator", () => {
201201
const operator = parseExpression({ avg: ["a", "b", "c"] });
202+
const decimalOperator = parseExpression({ avg: { data: ["a", "b", "c"], extra: { decimalPlaces: 3 } } });
202203

203204
it("should return avg", () => {
204205
expect(operator.evaluate({ a: 3, b: 4, c: 2 })).to.deep.equals(3);
@@ -215,5 +216,14 @@ describe("parseExpression", () => {
215216
it("returns 0 when no argument passed", () => {
216217
expect(operator.evaluate({})).to.deep.equals(0);
217218
});
219+
it("works with decimalPlaces specified", () => {
220+
expect(decimalOperator.evaluate({ a: 3, b: 2 })).to.deep.equals(2.5);
221+
});
222+
it("Correctly rounds to the right number of decimal places", () => {
223+
expect(decimalOperator.evaluate({ a: 2, b: 2, c: 1 })).to.deep.equals(1.667);
224+
});
225+
it("works with strings", () => {
226+
expect(decimalOperator.evaluate({ a: "2", b: "3" })).to.deep.equals(2.5);
227+
});
218228
});
219229
});

app/javascript/libs/expressions/utils/build-operator.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
avgOperator
1616
} from "../operators";
1717

18-
export default (operator, value) => {
18+
export default (operator, value, extra) => {
1919
switch (operator) {
2020
case COMPARISON_OPERATORS.EQ:
2121
return eqOperator(value);
@@ -38,7 +38,7 @@ export default (operator, value) => {
3838
case MATHEMATICAL_OPERATORS.SUM:
3939
return sumOperator(value);
4040
case MATHEMATICAL_OPERATORS.AVG:
41-
return avgOperator(value);
41+
return avgOperator(value, extra);
4242
default:
4343
throw Error(`Operator ${operator} is not valid.`);
4444
}

app/services/record_json_validator_service.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def build_schema(fields)
5454
when Field::TALLY_FIELD
5555
properties[field.name] = { 'type' => %w[object null], 'properties' => tally_properties(field.tally_i18n) }
5656
when Field::CALCULATED
57-
properties[field.name] = { 'type' => %w[integer string boolean array null],
57+
properties[field.name] = { 'type' => %w[integer number string boolean array null],
5858
'minimum' => -2_147_483_648,
5959
'maximum' => 2_147_483_647 }
6060
end

0 commit comments

Comments
 (0)