Skip to main content
Version: v0.0.3

Function: pickMrn()

pickMrn(identifiers): string | undefined

Pick the MRN string from a list of patientRole/id II identifiers.

Returns the first identifier's extension, or undefined when the list is empty, when that identifier has no extension (a root-only id names an assigning authority, not a patient), or when it carries a nullFlavor.

Why a nullFlavor-marked <id> is withheld rather than read. In HL7 v3, nullFlavor is a property of ANY: it marks an exceptional value, one with no proper value. An <id nullFlavor="UNK" extension="X"/> therefore says both "this identifier is unknown" and "this identifier is X", and parseIi already flags that contradiction as CONTRADICTORY_NULL_FLAVOR (safety-critical). Reading X out of it anyway is how a record gets filed against the wrong patient, which is silent, persistent, and contaminates everything downstream, so it sits alongside a wrong dose and a wrong code system in this package's harm ordering.

And why it is not skipped past, either. Falling through to the next <id> looks like the helpful move and is a worse one. CDA R2 makes patientRole/id 1..* and neither it nor C-CDA R2.1 says which entry is the MRN, so nothing in the document ranks them: the second id is not "another MRN", it is whatever the sending system listed second, commonly a plan member number, an account number, or the SSN under 2.16.840.1.113883.4.1. Substituting it would answer the MRN question confidently from a different assigning authority, with no signal naming the substitution, which trades one wrong-identifier failure for a quieter one. The rule this helper is applying declines a manufactured reading; it does not manufacture a replacement. Same slot, same position, reading withheld.

This is the PQ.value rule, applied one layer up rather than abandoned. parsePq withholds value on a contradicted quantity because value is a number the parser manufactured from raw, and raw survives, so nothing the document said is lost. II.extension is not like that: it is the document's own text, with no second copy, so parseIi keeps it and a caller reading patient.identifiers[0].extension still gets it with the nullFlavor sitting on the same object. What is manufactured here is the selection: picking one <id> out of a list and handing back a bare string strips away the nullFlavor that qualified it. So the derived act declines, and the verbatim data stays reachable. Both properties, rather than a trade between them.

A caller whose assigning authority genuinely carries the MRN on a later <id>, or who treats a given nullFlavor as tolerable, can walk patient.identifiers and branch on each root. That decision needs the authority OIDs, which this helper does not have and must not guess.

Provenance: no normative SHALL is cited. CDA R2 declares @nullFlavor and @extension on II independently, and neither CDA R2 nor C-CDA R2.1 states which patientRole/id is the MRN. The rule rests on v3 datatype semantics plus the harm ordering above.

Parameters

identifiers

readonly II[]

Returns

string | undefined

Example

import { pickMrn } from "@cosyte/ccda";
pickMrn([{ root: "2.16.840.1.113883.19.5", extension: "MRN001" }]);
// → "MRN001"

pickMrn([{ nullFlavor: "UNK", extension: "MRN001" }]);
// → undefined (the document disowned that identifier)

// A second id under a different authority is NOT a fallback MRN:
pickMrn([{ nullFlavor: "UNK", extension: "MRN001" }, { root: "2.16.840.1.113883.4.1", extension: "SYNTH-9" }]);
// → undefined (still the first id, withheld; never the next authority's number)

pickMrn([]);
// → undefined