Named Tables & Arrays
On this page
A markdown table is data. Give it a name and CalcMark can compute with its columns — sum a column, multiply two columns row by row, and write per-row results back into a table — without copying numbers into a calc block.
Naming a table #
Put a table: directive on the line before a markdown table. The directive is an HTML comment, so it is invisible in any markdown viewer; only the table shows.
<!-- table: rates (role, rate, hc) -->
| Role | Hourly rate | Headcount |
|--------|-------------|-----------|
| Senior | $250 | 2 |
| Junior | $150 | 5 |
- The table name (
rates) and the column names (role,rate,hc) come from the directive, not from the header row. Header text is free to say whatever reads best. - Names are lowercased and inner spaces become underscores:
<!-- table: Q1 Sales (Region, Total Rev) -->givesq1_sales.total_rev. - The directive must declare exactly as many columns as the table has. CalcMark reports a mismatch on the directive line.
- A table with no directive is ordinary markdown. Nothing changes for existing documents.
Reading columns #
Each column is an array — an ordered list of values of one kind. Read a column with a dot:
senior_rate = rates.rate → [$250.00, $150.00]
people = rates.hc → [2, 5]
Cells are parsed like any CalcMark literal: $250 is currency, 2 weeks is a duration, 10 GB/s is a rate, 3 is a number. A cell that is not a literal (Senior, n/a) is text: it can be counted and shown, but not used in arithmetic. Every cell in a column must be the same kind of value — a column mixing $100 and 50% is reported at the offending cell.
Arithmetic on columns #
Arithmetic between two columns works row by row; a scalar applies to every row:
line_costs = rates.rate * rates.hc → [$500.00, $750.00]
uplifted = rates.rate * 1.1 → [$275.00, $165.00]
Both arrays must have the same number of rows. All the usual rules apply per row — units convert, currencies must match, and a text column cannot take part.
Aggregates #
sum, avg, min, max, and count accept a single array:
total = sum(rates.rate * rates.hc) → $1,250.00
average_rate = avg(rates.rate) → $200.00
cheapest = min(rates.rate) → $150.00
largest_team = max(rates.hc) → 5
roles = count(rates.role) → 2
The scalar forms still work (sum(a, b, c), max(4, 9, 1)). A call takes either one array or several scalars, never a mix.
Showing results in a table #
Use {{name}} inside a markdown table row and each row receives its own element:
| Role | Line cost |
|--------|----------------|
| Senior | {{line_costs}} |
| Junior | {{line_costs}} |
renders as
| Role | Line cost |
|--------|-----------|
| Senior | $500.00 |
| Junior | $750.00 |
The array must have exactly one value per data row; otherwise the tags are left unresolved and CalcMark reports the mismatch on the table. Outside a table, {{line_costs}} and {{rates.rate}} render as a list: [$500.00, $750.00].
Order matters #
Tables register where they appear. Calc blocks below a table can read it; blocks above cannot. This is the same top-to-bottom rule as variables.
What is not supported #
- No array literals (
[1, 2, 3]) — arrays only come from tables. - No indexing or row lookup (
rates.rate[0], filtering). - No formulas inside cells — cells are literals; computation happens in calc blocks.
- No loading from files.
In Embedded mode #
In markdown-with-fences documents the directive and table live in the prose, and the fenced ```cm blocks below read them:
<!-- table: rates (rate, hc) -->
| Rate | HC |
|------|----|
| $250 | 2 |
```cm
total = sum(rates.rate * rates.hc)
```