csv
stableCSV parser, serialiser, and query toolkit for reading, writing, filtering, sorting, and grouping tabular data with configurable delimiters.
use plugin csv::{parse, read_file, stringify, …} Functions (14)
- parse Parse a CSV string into a table of rows
- read_file Read and parse a CSV file from disk
- stringify Serialise a table of rows to a CSV string
- write_file Write a table of rows to a CSV file
- parse_line Parse a single CSV line into fields
- count_rows Count the number of rows in a parsed table
- count_columns Count the number of columns in the first row
- get_column Extract all values for a named column
- filter_rows Filter rows by column value and operator
- sort_rows Sort rows by a column value
- unique_values Get distinct values for a column
- group_by Group rows by a column value
- get_headers Extract header names from the first row
- select_columns Project only specified columns from each row
Overview
The csv plugin reads, writes, and queries comma-separated (and other delimited)
data as plain Zolo tables. Parsing produces a table of rows keyed by 1-based
integers, where each row is itself a table keyed by column name (or by 1-based
index when headers are disabled). Because the parsed shape is just nested tables,
the query helpers — filtering, sorting, grouping, projection — all operate on the
same value and compose freely. Reach for it whenever you need to load tabular data
from a string or file, transform it, and serialise it back out, all without an
external dependency or an explicit schema.
Common patterns
Load a file, narrow it down, and write the result back:
use plugin csv::{read_file, filter_rows, select_columns, write_file}
let rows = read_file("/data/users.csv")
let adults = filter_rows(rows, "age", "gt", 18)
let slim = select_columns(adults, #{1: "name", 2: "email"})
write_file("/tmp/adults.csv", slim)
Parse a string, then rank and report on it:
use plugin csv::{parse, sort_rows, count_rows}
let rows = parse("name,score\nBob,87\nAlice,95\nCarol,92")
let ranked = sort_rows(rows, "score", false)
print("{count_rows(ranked)} players, top is {ranked[1]["name"]}")
Summarise a column by grouping distinct values:
use plugin csv::{parse, unique_values, group_by}
let rows = parse("dept,name\nEng,Alice\nHR,Bob\nEng,Carol")
print("departments: {#unique_values(rows, "dept")}")
let groups = group_by(rows, "dept")
print("first dept {groups[1]["key"]} has {#groups[1]["rows"]} people")
Parse a CSV string into a table of rows
Parses a CSV string and returns a table of row tables. With headers (default),
each row is keyed by column name. Without headers, keys are 1-based integers.
config is optional and accepts headers (bool) and delimiter (string).
use plugin csv::{parse}
let data = "name,age,city\nAlice,30,NYC\nBob,25,LA"
let rows = parse(data)
print(rows[1]["name"])
print(rows[2]["age"])
use plugin csv::{parse}
let tsv = "Alice\t30\nBob\t25"
let rows = parse(tsv, #{"headers": false, "delimiter": "\t"})
print(rows[1][1])
Read and parse a CSV file from disk
Reads a CSV file from path and returns the same structure as parse.
config is optional and accepts the same options as parse.
use plugin csv::{read_file}
let rows = read_file("/data/users.csv")
print("loaded {#rows} rows")
print(rows[1]["email"])
Serialise a table of rows to a CSV string
Serialises a table of row tables to a CSV string. Writes a header row from the string keys of the first row, then one data row per entry.
use plugin csv::{stringify}
let rows = #{
1: #{"name": "Alice", "age": "30"},
2: #{"name": "Bob", "age": "25"}
}
let csv = stringify(rows)
print(csv)
Round-tripping is lossless for shape: parse a string, transform it, and write it back out with a custom delimiter.
use plugin csv::{parse, sort_rows, stringify}
let rows = parse("name,age\nBob,25\nAlice,30")
let sorted = sort_rows(rows, "name", true)
print(stringify(sorted, #{"delimiter": ";"}))
Write a table of rows to a CSV file
Serialises rows to CSV and writes the result to the file at path.
use plugin csv::{write_file}
let rows = #{
1: #{"product": "Widget", "price": "9.99"},
2: #{"product": "Gadget", "price": "19.99"}
}
write_file("/tmp/products.csv", rows)
Parse a single CSV line into fields
Parses a single CSV line and returns a table of field values with 1-based
integer keys. delimiter defaults to ",".
use plugin csv::{parse_line}
let fields = parse_line('Alice,"New York",30')
print(fields[1])
print(fields[2])
Count the number of rows in a parsed table
Returns the number of rows in a parsed CSV table.
use plugin csv::{parse, count_rows}
let rows = parse("a,b\n1,2\n3,4\n5,6")
print("rows: {count_rows(rows)}")
Count the number of columns in the first row
Returns the number of columns in the first row of a parsed CSV table.
use plugin csv::{parse, count_columns}
let rows = parse("id,name,email,age\n1,Alice,a@x.com,30")
print("columns: {count_columns(rows)}")
Extract all values for a named column
Extracts all values for the named column and returns them as a 1-based table of strings.
use plugin csv::{parse, get_column}
let rows = parse("name,score\nAlice,95\nBob,87\nCarol,92")
let scores = get_column(rows, "score")
print(scores[1])
print(scores[2])
Filter rows by column value and operator
Returns only rows where the column matches the condition. Supported operators:
"eq", "neq", "gt", "lt", "contains". Numeric comparisons are
performed when both sides parse as floats.
use plugin csv::{parse, filter_rows}
let rows = parse("name,age\nAlice,30\nBob,17\nCarol,25")
let adults = filter_rows(rows, "age", "gt", 18)
print("adults: {#adults}")
let search = filter_rows(rows, "name", "contains", "li")
print(search[1]["name"])
Filters return the same row shape, so they chain — apply one condition, then another, to narrow the set step by step.
use plugin csv::{parse, filter_rows}
let rows = parse("name,dept,age\nAlice,Eng,30\nBob,Eng,17\nCarol,HR,40")
let eng = filter_rows(rows, "dept", "eq", "Eng")
let senior_eng = filter_rows(eng, "age", "gt", 18)
print("senior engineers: {#senior_eng}")
Sort rows by a column value
Returns rows sorted by the given column. Numeric sorting is used when values
parse as numbers; otherwise lexicographic. ascending defaults to true.
use plugin csv::{parse, sort_rows}
let rows = parse("name,score\nBob,87\nAlice,95\nCarol,92")
let ranked = sort_rows(rows, "score", false)
print("top scorer: {ranked[1]["name"]}")
When a column holds non-numeric text, sorting falls back to lexicographic order, so the same call alphabetises names ascending.
use plugin csv::{parse, sort_rows}
let rows = parse("name,score\nCarol,92\nAlice,95\nBob,87")
let alpha = sort_rows(rows, "name", true)
print(alpha[1]["name"])
print(alpha[2]["name"])
Get distinct values for a column
Returns a table of distinct values (in first-seen order) for the named column.
use plugin csv::{parse, unique_values}
let rows = parse("city\nNYC\nLA\nNYC\nChicago\nLA")
let cities = unique_values(rows, "city")
print("unique cities: {#cities}")
Group rows by a column value
Groups rows by the named column. Returns a table of {key, rows} entries,
one per distinct value, preserving insertion order.
use plugin csv::{parse, group_by}
let rows = parse("dept,name\nEng,Alice\nHR,Bob\nEng,Carol")
let groups = group_by(rows, "dept")
print("groups: {#groups}")
print("first group key: {groups[1]["key"]}")
print("members: {#groups[1]["rows"]}")
Each group's rows is itself a parsed CSV table, so the query helpers work on a
single group just as they do on the whole dataset.
use plugin csv::{parse, group_by, count_rows}
let rows = parse("dept,name\nEng,Alice\nHR,Bob\nEng,Carol\nEng,Dan")
let groups = group_by(rows, "dept")
for g in groups {
print("{g["key"]}: {count_rows(g["rows"])}")
}
Extract header names from the first row
Extracts the column header names from the first row of a parsed CSV table. Returns a 1-based table of strings.
use plugin csv::{parse, get_headers}
let rows = parse("id,name,email\n1,Alice,a@x.com")
let headers = get_headers(rows)
print(headers[1])
print(headers[2])
print(headers[3])
Project only specified columns from each row
Projects only the specified columns from each row. columns is a table of
column name strings. Columns not in the list are dropped.
use plugin csv::{parse, select_columns}
let rows = parse("id,name,email,age\n1,Alice,a@x.com,30")
let slim = select_columns(rows, #{1: "name", 2: "email"})
print(slim[1]["name"])
print(slim[1]["email"])