Skip to content

csv

stable

CSV parser, serialiser, and query toolkit for reading, writing, filtering, sorting, and grouping tabular data with configurable delimiters.

use plugin csv::{parse, read_file, stringify, …}
14 functions Data Formats
/ filter jk navigate Esc clear
Functions (14)
  1. parse Parse a CSV string into a table of rows
  2. read_file Read and parse a CSV file from disk
  3. stringify Serialise a table of rows to a CSV string
  4. write_file Write a table of rows to a CSV file
  5. parse_line Parse a single CSV line into fields
  6. count_rows Count the number of rows in a parsed table
  7. count_columns Count the number of columns in the first row
  8. get_column Extract all values for a named column
  9. filter_rows Filter rows by column value and operator
  10. sort_rows Sort rows by a column value
  11. unique_values Get distinct values for a column
  12. group_by Group rows by a column value
  13. get_headers Extract header names from the first row
  14. 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,[email protected],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,[email protected]")
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,[email protected],30")
let slim = select_columns(rows, #{1: "name", 2: "email"})
print(slim[1]["name"])
print(slim[1]["email"])
enespt-br