The data model
A SimpleCsv is a two-dimensional slice of strings:
type SimpleCsv [][]stringThe first row is always the header row. A small file looks like this:
row 0: id, name, status
row 1: 101, Ana, active
row 2: 102, Ben, pausedInvariants
| Guarantee | What it means in a CLI |
|---|---|
| Unique headers | Name-based APIs always address exactly one column. Duplicate headers are rejected when reading, creating, or renaming. |
| Uniform width | Every row has the same number of cells as the header. Ragged input is rejected instead of silently shifting values. |
| All strings | Numbers, dates, and booleans stay strings in storage. Parse only inside the operation that needs a typed value. |
| Copy-on-write | Methods that change data return independent CSVs. Getters return copies, too, so callers cannot mutate internal rows by accident. |
Field-first APIs
Column positions are useful at the lowest level, but most scripts should name the field they mean. These methods skip the header for you:
Keep or remove
WhereWhereFoldFilterByFieldUniqueByFields
Change values
MapColumnByFieldFillColumnByFieldReplaceInFieldTrimSpace
Build shape
OnlyThisFieldsRenameHeadersMoveColumnAddComputedColumn
Inspect values
DistinctValueCountsGetColumnByFieldEachDataRow
Case sensitivity
Equality filters are explicit: Where is case-sensitive, while WhereFold uses case-insensitive equality. The older find and match methods are case-insensitive by default; use their CaseSensitive variants when exact matching matters.
Safe input and output
Normal reads load the full CSV into memory, which keeps the API simple. For large or attacker-controlled input, use ReadCsvLimit with record and byte limits.
Writes are atomic and use mode 0600. Values are written verbatim by default. If output may be opened in a spreadsheet and contains untrusted values, call SanitizeFormulas before writing.
safe := input.SanitizeFormulas()
if err := safe.WriteCsvFileE("for-spreadsheet.csv"); err != nil {
log.Fatal(err)
}Booleans or errors
Short transforms often read cleanly with a result and an ok boolean:
filtered, ok := input.Where("status", "active")When a failed field lookup or header collision needs to reach a user, use the selective error variants:
joined, err := customers.JoinE(orders, "id", "customer_id")
if err != nil {
log.Fatal(err)
}See the complete list on the API map.