Skip to content

Commit c405400

Browse files
ruscoderclaude
andcommitted
#68: Support for coalesce function
Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 1d1d655 commit c405400

7 files changed

Lines changed: 57 additions & 1 deletion

File tree

fhirpathpy/engine/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ def doInvoke(ctx, fn_name, data, raw_params):
6464
if "nullable_input" in invocation and util.is_nullable(data):
6565
return []
6666

67+
if "variadic" in invocation:
68+
tp = invocation["variadic"]
69+
raw_params_list = raw_params if isinstance(raw_params, list) else []
70+
thisValue = ctx["$this"] if "$this" in ctx else ctx["dataRoot"]
71+
params = [make_param(ctx, thisValue, tp, pr) for pr in raw_params_list]
72+
res = invocation["fn"](ctx, util.arraify(data), *params)
73+
return util.arraify(res)
74+
6775
if "arity" not in invocation:
6876
if raw_params is None or util.is_empty(raw_params):
6977
res = invocation["fn"](ctx, util.arraify(data))

fhirpathpy/engine/invocations/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"skip": {"fn": filtering.skip_fn, "arity": {1: ["Integer"]}},
5151
"intersect": {"fn": subsetting.intersect_fn, "arity": {1: ["AnyAtRoot"]}},
5252
"combine": {"fn": combining.combine_fn, "arity": {1: ["AnyAtRoot"]}},
53+
"coalesce": {"fn": combining.coalesce_fn, "variadic": "Expr"},
5354
"iif": {"fn": misc.iif_macro, "arity": {2: ["Expr", "Expr"], 3: ["Expr", "Expr", "Expr"]}},
5455
"trace": {"fn": misc.trace_fn, "arity": {0: [], 1: ["String"]}},
5556
"toInteger": {"fn": misc.to_integer},

fhirpathpy/engine/invocations/combining.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from fhirpathpy.engine import util
12
from fhirpathpy.engine.invocations import existence
23

34
"""
@@ -15,3 +16,11 @@ def combine_fn(ctx, coll1, coll2):
1516

1617
def exclude_fn(ctx, coll1, coll2):
1718
return [element for element in coll1 if element not in coll2]
19+
20+
21+
def coalesce_fn(ctx, data, *exprs):
22+
for expr in exprs:
23+
result = expr(data)
24+
if not util.is_empty(result):
25+
return result
26+
return []

fhirpathpy/engine/invocations/navigation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def func(acc, res):
5858
actualTypes = model["choiceTypePaths"].get(altPropName, [])
5959
if len(actualTypes) > 0:
6060
# If it is, we can use it
61-
fullPath = f"{res.propName}.{prop[:-len(childPath)]}"
61+
fullPath = f"{res.propName}.{prop[: -len(childPath)]}"
6262

6363
if isinstance(value, list):
6464
mapped = [

fhirpathpy/engine/nodes.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,8 @@ def __str__(self):
646646
return time_str
647647
return self.asStr
648648

649+
__hash__ = None
650+
649651
def __eq__(self, other):
650652
if isinstance(other, str):
651653
return self.getTimeMatchStr()
@@ -734,6 +736,8 @@ def __str__(self):
734736
return iso_str
735737
return self.asStr
736738

739+
__hash__ = None
740+
737741
def __eq__(self, other):
738742
if isinstance(other, str):
739743
return self.getDateTimeMatchStr()

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
minversion = "6.0"
33
addopts = "-ra -q --color=yes --cov=fhirpathpy --cov-report=xml"
44
testpaths = ["tests"]
5+
filterwarnings = ["ignore::pytest.PytestRemovedIn9Warning"]
56
log_cli = true
67
log_cli_level = "INFO"
78
python_functions = "*_test"

tests/cases/5.2.8_coalesce.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
tests:
2+
- desc: '5.2.8 coalesce'
3+
4+
- desc: '** returns first non-empty value from identifier'
5+
expression: "coalesce(Patient.identifier.where(system='required-system').value.first(), 'unknown')"
6+
result:
7+
- required-value
8+
9+
- desc: '** returns fallback when first is empty'
10+
expression: "coalesce(Patient.identifier.where(system='other-system').value.first(), 'unknown')"
11+
result:
12+
- unknown
13+
14+
- desc: '** returns first non-empty collection among multiple args'
15+
expression: "coalesce({}, (1 | 2), (3 | 4))"
16+
result:
17+
- 1
18+
- 2
19+
20+
- desc: '** returns single value when first arg is empty'
21+
expression: "coalesce({}, 1)"
22+
result:
23+
- 1
24+
25+
- desc: '** returns empty when only arg is empty'
26+
expression: "coalesce({})"
27+
result: []
28+
29+
subject:
30+
resourceType: Patient
31+
identifier:
32+
- system: required-system
33+
value: required-value

0 commit comments

Comments
 (0)