Would anyone be interested in me making a package out of the following table utility I hashed together?
It takes data in an array of records format, then column definitions, including a function used to determine the contents of each cell, which could be just passing through the value of a record property, or whatever function or formatting you can express in Typst.
You can also pass in any arguments you'd pass into the standard table function.
It will also generate column widths for each column, either auto if not defined, or whatever you define in the column definition.
I just threw this together last night, so there's a few things to improve or cleanup if I made it a public package.
#let tablez(data, colDefs, ..tableArgs) = {
let colWidths = ()
for colDef in colDefs {
if colDef.keys().contains("width") {
colWidths.push(colDef.width);
} else {
colWidths.push(auto);
}
}
let colAlignments = ()
for colDef in colDefs {
if colDef.keys().contains("align") {
colAlignments.push(colDef.align);
} else {
colAlignments.push(auto);
}
}
let entries = ()
for colDef in colDefs {
entries.push(colDef.label);
}
let i = 0;
for record in data {
record.insert("index", i);
i = i + 1;
for colDef in colDefs {
entries.push([#(colDef.cellValueFunc)(record)])
}
}
set align(center);
table(
columns: colWidths,
align: colAlignments,
..tableArgs,
..entries
)
}
#let tableData = (
(name_first: "Alice", name_last: "Wright", position: "Manager", office_code: 0),
(name_first: "Bob", name_last: "Smith", position: "Sales Representative", office_code: 1),
(name_first: "Charlie", name_last: "Brown", position: "System Administrator", office_code: 0),
(name_first: "Diana", name_last: "Ross", position: "Recruiter", office_code: 2),
(name_first: "Evan", name_last: "Johnson", position: "Manager", office_code: 1)
)
#let office_locations = (
"Headquarters",
"Regional Office",
"International Branch"
)
#tablez(
tableData,
(
(label: [], cellValueFunc: r => { r.index }), // index is automatically generated for each row
(label: [*Full Name*], width: 1fr, cellValueFunc: r => { [#r.name_first #r.name_last] }), // concatenation
(label: [*Office*], width: 1fr, cellValueFunc: r => { office_locations.at(r.office_code) }), // reference external data/enum
),
fill: (_, row) => if calc.odd(row) { luma(30) } else { luma(45) }, // Table arguments
stroke: none // Table arguments
)
#tablez(
tableData.filter(r => r.position == "Manager").sorted(key: r => r.name_last), // filter and sort data, all native array functions
(
(label: [*Managers, by Last Name*], width: 1fr, align: left, cellValueFunc: r => { [#r.name_first #r.name_last] }), // alignment
(label: [*Office*], cellValueFunc: r => { office_locations.at(r.office_code) }),
),
fill: (_, row) => if calc.odd(row) { luma(30) } else { luma(45) },
stroke: none
)