cql-execution
Version:
An execution framework for the Clinical Quality Language (CQL)
243 lines • 8.01 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lessThan = lessThan;
exports.lessThanOrEquals = lessThanOrEquals;
exports.greaterThan = greaterThan;
exports.greaterThanOrEquals = greaterThanOrEquals;
exports.equivalent = equivalent;
exports.equals = equals;
const datatypes_1 = require("../datatypes/datatypes");
function areNumbers(a, b) {
return typeof a === 'number' && typeof b === 'number';
}
function areStrings(a, b) {
return typeof a === 'string' && typeof b === 'string';
}
function areDateTimesOrQuantities(a, b) {
return ((a && a.isDateTime && b && b.isDateTime) ||
(a && a.isDate && b && b.isDate) ||
(a && a.isQuantity && b && b.isQuantity));
}
function isUncertainty(x) {
return x instanceof datatypes_1.Uncertainty;
}
function lessThan(a, b, precision) {
if (areNumbers(a, b) || areStrings(a, b)) {
return a < b;
}
else if (areDateTimesOrQuantities(a, b)) {
return a.before(b, precision);
}
else if (isUncertainty(a)) {
return a.lessThan(b);
}
else if (isUncertainty(b)) {
return datatypes_1.Uncertainty.from(a).lessThan(b);
}
else {
return null;
}
}
function lessThanOrEquals(a, b, precision) {
if (areNumbers(a, b) || areStrings(a, b)) {
return a <= b;
}
else if (areDateTimesOrQuantities(a, b)) {
return a.sameOrBefore(b, precision);
}
else if (isUncertainty(a)) {
return a.lessThanOrEquals(b);
}
else if (isUncertainty(b)) {
return datatypes_1.Uncertainty.from(a).lessThanOrEquals(b);
}
else {
return null;
}
}
function greaterThan(a, b, precision) {
if (areNumbers(a, b) || areStrings(a, b)) {
return a > b;
}
else if (areDateTimesOrQuantities(a, b)) {
return a.after(b, precision);
}
else if (isUncertainty(a)) {
return a.greaterThan(b);
}
else if (isUncertainty(b)) {
return datatypes_1.Uncertainty.from(a).greaterThan(b);
}
else {
return null;
}
}
function greaterThanOrEquals(a, b, precision) {
if (areNumbers(a, b) || areStrings(a, b)) {
return a >= b;
}
else if (areDateTimesOrQuantities(a, b)) {
return a.sameOrAfter(b, precision);
}
else if (isUncertainty(a)) {
return a.greaterThanOrEquals(b);
}
else if (isUncertainty(b)) {
return datatypes_1.Uncertainty.from(a).greaterThanOrEquals(b);
}
else {
return null;
}
}
function equivalent(a, b) {
if (a == null && b == null) {
return true;
}
if (a == null || b == null) {
return false;
}
if (isCode(a)) {
return codesAreEquivalent(a, b);
}
// Quantity equivalence is the same as Quantity equality
if (a.isQuantity) {
return a.equals(b);
}
// Use overloaded 'equivalent' function if it is available
if (typeof a.equivalent === 'function') {
return a.equivalent(b);
}
const [aClass, bClass] = getClassOfObjects(a, b);
switch (aClass) {
case '[object Array]':
return compareEveryItemInArrays(a, b, equivalent);
case '[object Object]':
return compareObjects(a, b, equivalent);
case '[object String]':
// Make sure b is also a string
if (bClass === '[object String]') {
// String equivalence is case- and locale insensitive
a = a.replace(/\s/g, ' ');
b = b.replace(/\s/g, ' ');
return a.localeCompare(b, 'en', { sensitivity: 'base' }) === 0;
}
break;
}
return equals(a, b);
}
function isCode(object) {
return object.hasMatch && typeof object.hasMatch === 'function';
}
function codesAreEquivalent(code1, code2) {
return code1.hasMatch(code2);
}
function getClassOfObjects(object1, object2) {
return [object1, object2].map(obj => ({}).toString.call(obj));
}
function compareEveryItemInArrays(array1, array2, comparisonFunction) {
return (array1.length === array2.length &&
array1.every((item, i) => comparisonFunction(item, array2[i])));
}
function compareObjects(a, b, comparisonFunction) {
if (!classesEqual(a, b)) {
return false;
}
return deepCompareKeysAndValues(a, b, comparisonFunction);
}
function classesEqual(object1, object2) {
return object2 instanceof object1.constructor && object1 instanceof object2.constructor;
}
function deepCompareKeysAndValues(a, b, comparisonFunction) {
let finalComparisonResult;
const aKeys = getKeysFromObject(a).sort();
const bKeys = getKeysFromObject(b).sort();
// Array.every() will only return true or false, so set a flag for if we should return null
let shouldReturnNull = false;
// Check if both arrays of keys are the same length and key names match
if (aKeys.length === bKeys.length && aKeys.every((value, index) => value === bKeys[index])) {
finalComparisonResult = aKeys.every(key => {
// if both are null we should return true to satisfy ignoring empty values in tuples
if (a[key] == null && b[key] == null) {
return true;
}
const comparisonResult = comparisonFunction(a[key], b[key]);
if (comparisonResult === null) {
shouldReturnNull = true;
return true;
}
return comparisonResult;
});
}
else {
finalComparisonResult = false;
}
if (finalComparisonResult && shouldReturnNull) {
return null;
}
return finalComparisonResult;
}
function getKeysFromObject(object) {
return Object.keys(object).filter(k => !isFunction(object[k]));
}
function isFunction(input) {
return input instanceof Function || {}.toString.call(input) === '[object Function]';
}
function equals(a, b) {
// Handle null cases first: spec says if either is null, return null
if (a == null || b == null) {
return null;
}
// If one is a Quantity, use the Quantity equals function
if (a && a.isQuantity) {
return a.equals(b);
}
// If one is a Ratio, use the ratio equals function
if (a && a.isRatio) {
return a.equals(b);
}
// If one is an Uncertainty, convert the other to an Uncertainty
if (a instanceof datatypes_1.Uncertainty) {
b = datatypes_1.Uncertainty.from(b);
}
else if (b instanceof datatypes_1.Uncertainty) {
a = datatypes_1.Uncertainty.from(a);
}
// Use overloaded 'equals' function if it is available
if (typeof a.equals === 'function') {
return a.equals(b);
}
// Return true of the objects are primitives and are strictly equal
if ((typeof a === typeof b && typeof a === 'string') ||
typeof a === 'number' ||
typeof a === 'boolean') {
return a === b;
}
// Return false if they are instances of different classes
const [aClass, bClass] = getClassOfObjects(a, b);
if (aClass !== bClass) {
return false;
}
switch (aClass) {
case '[object Date]':
// Compare the ms since epoch
return a.getTime() === b.getTime();
case '[object RegExp]':
// Compare the components of the regular expression
return ['source', 'global', 'ignoreCase', 'multiline'].every(p => a[p] === b[p]);
case '[object Array]':
if (a.indexOf(null) >= 0 ||
a.indexOf(undefined) >= 0 ||
b.indexOf(null) >= 0 ||
b.indexOf(undefined) >= 0) {
return null;
}
return compareEveryItemInArrays(a, b, equals);
case '[object Object]':
return compareObjects(a, b, equals);
case '[object Function]':
return a.toString() === b.toString();
}
// If we made it this far, we can't handle it
return false;
}
//# sourceMappingURL=comparison.js.map