Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion src/elm/list.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Set as ImmutableSet } from 'immutable';
import { Context } from '../runtime/context';
import { equals } from '../util/comparison';
import { equals, equivalent } from '../util/comparison';
import { NormalizedKey, toNormalizedKey } from '../util/immutableUtil';
import { typeIsArray } from '../util/util';
import { build } from './builder';
Expand Down Expand Up @@ -141,6 +141,42 @@ export function doContains(container: any[], item: any) {
);
}

function doEquivalentContains(container: any[], item: any) {
return container.some((element: any) => equivalent(element, item));
}

// Unlike plain Contains, EquivalentContains is only used for Lists, not Intervals,
// and the ~contains operator with ValueSet/CodeSystem maps to the "InValueSet"/"InCodeSystem" expressions, not this
export class EquivalentContains extends Expression {
constructor(json: any) {
super(json);
}

async exec(ctx: Context) {
const [container, item] = await this.execArgs(ctx);
if (container == null) {
return false;
}
return doEquivalentContains(container, item);
}
}

// Unlike plain In, EquivalentIn is only used for Lists, not Intervals,
// and the ~in operator with ValueSet/CodeSystem maps to the "InValueSet"/"InCodeSystem" expressions, not this
export class EquivalentIn extends Expression {
constructor(json: any) {
super(json);
}

async exec(ctx: Context) {
const [item, container] = await this.execArgs(ctx);
if (container == null) {
return false;
}
return doEquivalentContains(container, item);
}
}

// Delegated to by overloaded#ProperContains and overloaded#ProperIn
export function doProperContains(container: any[], item: any) {
// The "proper" membership operators have list semantics, not set semantics.
Expand Down
24 changes: 24 additions & 0 deletions test/elm/list/data.cql
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,30 @@ define InNull: (null as List<Integer>) properly includes 1
define NullIn: {1, 2, null, 3} properly includes null
define NullNotIn: {1, 2, 3} properly includes null

// @Test: EquivalentContains
define ContainsEquivalentNotEqual: { 'A', 'B', 'C' } ~contains 'a'
define ContainsEquivalentAndEqual: { 'A', 'B', 'C' } ~contains 'A'
define ContainsEquivalentNotEqualQuantity: { 100 'cm', 200 'cm', 300 'cm' } ~contains 1 'm'
define ContainsEqualQuantity: { 100 'cm', 200 'cm', 300 'cm' } ~contains 100 'cm'
define ContainsEquivalentNotEqualDecimal: { 1.01, 2.02, 3.03 } ~contains 1.0
define DoesNotContain: { 'B', 'C' } ~contains 'a'
define DoesNotContainQuantity: { 200 'cm', 300 'cm' } ~contains 100 'cm'
define NullContainer: null ~contains 'a'
define DoesNotContainNull: { 'A', 'B', 'C' } ~contains null
define DoesContainNull: { 'A', 'B', 'C', null } ~contains null

// @Test: EquivalentIn
define ContainsEquivalentNotEqual: 'a' ~in { 'A', 'B', 'C' }
define ContainsEquivalentAndEqual: 'A' ~in { 'A', 'B', 'C' }
define ContainsEquivalentNotEqualQuantity: 1 'm' ~in { 100 'cm', 200 'cm', 300 'cm' }
define ContainsEqualQuantity: 100 'cm' ~in { 100 'cm', 200 'cm', 300 'cm' }
define ContainsEquivalentNotEqualDecimal: 1.0 ~in { 1.01, 2.02, 3.03 }
define DoesNotContain: 'a' ~in { 'B', 'C' }
define DoesNotContainQuantity: 100 'cm' ~in { 200 'cm', 300 'cm' }
define NullContainer: 'a' ~in null
define DoesNotContainNull: null ~in { 'A', 'B', 'C' }
define DoesContainNull: null ~in { 'A', 'B', 'C', null }

// @Test: Flatten
define ListOfLists: flatten { {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {9, 8, 7, 6, 5}, {4}, {3, 2, 1} }
define NullValue: flatten null
Expand Down
Loading
Loading