-
Notifications
You must be signed in to change notification settings - Fork 49
Description
The built-in list functions max, min, sum and mean currently only accept a single argument, a list of items to be aggregated.
The DMN 1.2 specification, section 10.3.4.4 permits a flattened list of arguments of arbitrary length to be supplied instead of a list.
For example both these should work:
- max([1,2,3]) = 3
- max(1,2,3) = 3
The following proposed change to utils\built-in-functions\list-functions\index.js would accomplish this.
Replace min, max, sum, and mean imlementations with this:
// Heuristic to determine if an object is a context or not.
// If so, it will have had the built-in functions added to it.
function isContext(obj) {
return 'max' in obj && 'substring' in obj;
}
const aggregateHelper = (aggregatorFunc, itemsOrList) => {
if (itemsOrList.length === 1) {
// One item in array, so require it to be an array.
if (!Array.isArray(itemsOrList[0])) {
try {
return aggregatorFunc([itemsOrList[0]]);
} catch (err) {
throw new Error('operation unsupported on element of this type');
}
} else {
return aggregatorFunc(itemsOrList[0]);
}
} else if (itemsOrList.length === 2 && isContext(itemsOrList[1])) {
// Two items in array, but the second one is a context, so only aggregate the first.
try {
if (!Array.isArray(itemsOrList[0])) {
return aggregatorFunc([itemsOrList[0]]);
}
return aggregatorFunc(itemsOrList[0]);
} catch (err) {
throw new Error('operation unsupported on element of this type');
}
} else {
try {
if (isContext(itemsOrList[itemsOrList.length - 1])) {
return aggregatorFunc(itemsOrList.slice(0, itemsOrList.length - 1));
}
return aggregatorFunc(itemsOrList);
} catch (err) {
throw new Error('operation unsupported on element of this type');
}
}
};
// Permits min(n1, n2, n3, ...) or min(list)
const min = (...itemsOrList) => aggregateHelper(arr => _.min(arr), itemsOrList);
// Permits max(n1, n2, n3, ...) or max(list)
const max = (...itemsOrList) => aggregateHelper(arr => _.max(arr), itemsOrList);
// Permits sum(n1, n2, n3, ...) or sum(list)
const sum = (...itemsOrList) => aggregateHelper(arr => _.sum(arr), itemsOrList);
const flatCount = (...itemsOrList) => aggregateHelper(arr => arr.length, itemsOrList);
// Permits mean(n1, n2, n3, ...) or mean(list)
const mean = (...itemsOrList) => {
const itemSum = sum(...itemsOrList);
const itemCount = flatCount(...itemsOrList);
return itemSum / itemCount;
};
NOTE on aggregateHelper:
This function works around the implementation details. The context object is passed to all these methods as a hidden extra argument. By using the rest operator to get all arguments (since the arguments variable is not available in a lambda function) we are also slurping up that context, if present. This function checks for the context and removes it from the arguments to be passed on to lodash.