Built-in sequences
TDC creates a few sequences automatically — they're available without being declared in
<env>. By convention their names start with an underscore. Use them in
interpolation (${{_count}}) and in if
expressions (if="_first").
Often what you need isn't the record's data but its place in the set — which number it is, whether it's first or last, how many there are in total. TDC works all of that out and hands it to you under four ready-made names.
The list
| Name | Value |
|---|---|
_count | The current record number, starting at 1 |
_first | "true" on the first record, "false" otherwise |
_last | "true" on the last record, "false" otherwise |
_total | The total number of records (the same as count), on every row |
_first and _last are the strings "true" and "false", not booleans. That's
deliberate: ${{_last}} prints as the readable word "false" (useful for
"isLast": ${{_last}} in JSON), while if="!_last" still reads it correctly as false.
All four at once
<block>
<line><data>_count=${{_count}} _first=${{_first}} _last=${{_last}} _total=${{_total}} ${{Name}}</data></line>
</block>
_count=1 _first=true _last=false _total=5 William _count=2 _first=false _last=false _total=5 Michael _count=3 _first=false _last=false _total=5 Robert _count=4 _first=false _last=false _total=5 John _count=5 _first=false _last=true _total=5 James
_count runs 1..5, _total is 5 on every row, _first is true only on the first
record, and _last only on the last.
Common uses
Numbering — ${{_count}} of ${{_total}}: ${{Name}}.
JSON without a trailing comma — a second <data if="!_last">, prints a comma on
every record except the last:
<line><data>{"id": ${{_count}}, "name": "${{Name}}"}</data><data if="!_last">,</data></line>
{"id": 1, "name": "Braylen"},
{"id": 2, "name": "Amiri"},
{"id": 3, "name": "Andre"},
{"id": 4, "name": "Izaiah"}A header only on the first record — <line if="_first"><data>=== START ===</data></line>.
Highlight the second half — the built-ins work inside expression arithmetic, so
if="_count * 2 > _total" turns true once the row number passes the midpoint.
Inside an each= line: _item and _item_id
A <line each="List"> repeats once per element of a list,
and two more built-ins are available only there — see
Relational tables for the full pattern:
| Name | Value |
|---|---|
_item | The position within this record — 1, 2, 3, restarting on each record |
_item_id | A number unique across the whole run — a ready-made primary key |
They exist only on an each= line. On an ordinary line the name resolves to nothing, and
TDC193 refuses the config rather than printing ${{_item}} literally. On a
line walking a pool, both are the record's position in the pool.
The walked name's positions: X._item, X._first, X._last
The list or pool an each= line walks lends its name to three more:
| Name | Value |
|---|---|
Orders._item | The number of this line among the lines the each prints: 1, 2, 3 |
Orders._first | true on the first line printed, false on the rest |
Orders._last | true on the last line printed, false on the rest |
They count the lines printed: an if= on the each line drops some elements, and the
positions skip them, so Orders._last is true on the last line that actually appears. That
makes <data if="!Orders._last">,</data> a comma between elements and never after the last
one. They differ from _first and _last above, which mark the first and last record
of the run. The line's own if= cannot read them (TDC215): they are counted
after it has decided.
Reserved names
Don't prefix your own sequences with _ — that prefix is reserved. You can't shadow a
built-in: TDC refuses to start and tells you which names are taken.
error[TDC033]: sequence name "_count" collides with a builtin note: Builtins: _count, _first, _last, _total. Pick a different name.
See also
- Output & formatting — interpolation and
if.