Read a result safely
A result record carries its fields as the analyzer sent them, plus a modeled view of the three that decide whether you can act on the value: the status, the abnormal flag and the reference range. This page reads all of them. It assumes one message with one patient; for a stream that carries more, read Read a stream that carries several messages first.
Read the value, units and test code
import { parseAstmRecords, results } from "@cosyte/astm";
const raw =
"H|\\^&\rP|1|PRAC-0001|LAB-0009\rO|1|SPEC-7||^^^687\rR|1|^^^687|28.6|U/L|10-40|N||F\rL|1|N\r";
const r = results(parseAstmRecords(raw))[0];
r?.value; // => "28.6"
r?.units; // => "U/L"
r?.universalTestId?.localCode; // => "687"
r?.referenceRange; // => "10-40"
The value and the units are strings, exactly as sent. Units are the analyzer's own free text: they
are not UCUM, and nothing converts or normalizes them. A numeric value with no units raises
ASTM_RECORD_UNITS_ABSENT, and the units stay empty rather than defaulted.
A value carrying an unescaped component delimiter (POS^1) keeps its whole text in value, has the
split parts in valueComponents, and raises ASTM_RECORD_AMBIGUOUS_VALUE_SPLIT, so you choose which
reading to use.
Check the status before you use the value
status is always present. Gate on status.isActiveFinal, which is true only for a plain F:
import { parseAstmRecords, results } from "@cosyte/astm";
const correction = "H|\\^&\rR|1|^^^687|30.1|U/L|10-40|H||C\rL|1\r";
const noStatus = "H|\\^&\rR|1|^^^687|30.1|U/L|10-40|H\rL|1\r";
const c = results(parseAstmRecords(correction))[0];
c?.status.meaning; // => "correction"
c?.status.isActiveFinal; // => false
c?.status.supersedes; // => true
const n = results(parseAstmRecords(noStatus))[0];
n?.status.meaning; // => "unspecified"
n?.status.isActiveFinal; // => false
supersedesistruefor a correction (C): this value replaces one sent earlier, so update what you stored rather than adding a second value.cancelledistrueforX: the test could not be done.- A missing status is
unspecifiedand an unknown letter isundefined; neither is ever read as final.
Result flags and statuses lists every status letter and its meaning.
Read the abnormal flag
import { parseAstmRecords, results } from "@cosyte/astm";
const raw = "H|\\^&\rR|1|^^^688|5.9|mmol/L|3.5-5.1|HU||F\rR|2|^^^689|7|U/L||ZZ||F\rL|1\r";
const [high, unknown] = results(parseAstmRecords(raw));
high?.flag?.meaning; // => "significantly-high"
high?.flag?.recognized; // => true
unknown?.flag?.meaning; // => "undefined"
unknown?.flag?.recognized; // => false
unknown?.flag?.raw; // => "ZZ"
An unrecognized flag reads undefined, never normal, keeps its raw text, and raises
ASTM_RECORD_UNDEFINED_ABNORMAL_FLAG. Matching is exact after the surrounding whitespace is trimmed,
so hu does not match HU. If your instrument sends lower case, change the case yourself before you
rely on the match.
Read the reference range
import { parseAstmRecords, results } from "@cosyte/astm";
const raw =
"H|\\^&\rR|1|^^^687|28.6|U/L|10-40|N||F\rR|2|^^^688|4|mg/L|<5|N||F\rR|3|^^^689|9|U/L|see note|N||F\rL|1\r";
const [closed, openLow, unparsed] = results(parseAstmRecords(raw));
closed?.range; // => { raw: "10-40", low: "10", high: "40", kind: "closed" }
openLow?.range; // => { raw: "<5", high: "5", kind: "open-low" }
unparsed?.range; // => { raw: "see note", kind: "unparsed" }
Bounds stay strings, so nothing is rounded. Text that is not low-high, <high or >low is
unparsed, with no bound invented, and raises ASTM_RECORD_UNPARSEABLE_REFERENCE_RANGE.
See what a flag or status was graded against
Each interpreted flag and status says which vocabulary it was compared with, whether or not the letter was recognized:
import { parseAstmRecords, results } from "@cosyte/astm";
const r = results(parseAstmRecords("H|\\^&\rR|1|^^^687|28.6|U/L|10-40|N||F\rL|1\r"))[0];
r?.flag?.vocabulary.attributed; // => true
r?.flag?.vocabulary.system; // => "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation"
r?.status.vocabulary.attributed; // => false
The flag's version is the version we compared against, not a claim about what the sender meant.
The status letters are attributed to nothing, and status.vocabulary.reason says why. What it
does, and does not do explains both.
Read the patient and the comments
The patient record keeps the practice-assigned and laboratory-assigned IDs apart, and a comment record attaches to the record before it:
import { commentsFor, parseAstmRecords, patient, results } from "@cosyte/astm";
const raw =
"H|\\^&\rP|1|PRAC-0001|LAB-0009||DOE^JANE^A||19700101|F\r" +
"R|1|^^^687|28.6|U/L|10-40|N||F\rC|1|I|Hemolyzed sample|G\rL|1|N\r";
const msg = parseAstmRecords(raw);
const p = patient(msg);
p?.practiceAssignedId; // => "PRAC-0001"
p?.laboratoryAssignedId; // => "LAB-0009"
p?.name?.last; // => "DOE"
p?.birthDate?.precision; // => "day"
const r = results(msg)[0];
const notes = r === undefined ? [] : commentsFor(msg, r);
notes.map((c) => c.text); // => ["Hemolyzed sample"]
A comment with no record before it to attach to is kept, attached to the message itself
(attachedToRoot: true), and raises ASTM_RECORD_ORPHAN_COMMENT. Dates are covered in Read a
timestamp without inventing a timezone.