csv
stableCSV parser, serialiser, and query toolkit for reading, writing, filtering, sorting, and grouping tabular data with configurable delimiters.
use plugin csv Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.
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
From import to result
- 01Import only what you use
- 02Copy the smallest example
- 03Run it with the Zolo CLI
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: {len(unique_values(rows, "dept"))}")
let groups = group_by(rows, "dept")
print("first dept {groups[1]["key"]} has {len(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 {len(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(r#"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: {len(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: {len(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: {len(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: {len(groups)}")
print("first group key: {groups[1]["key"]}")
print("members: {len(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"])