Skip to content

1.7.0 - mojo 1.1.0

Choose a tag to compare

@Phelsong Phelsong released this 26 Sep 00:27
· 2 commits to main since this release
4179764

Highlights:

  • Compiles on mojo 1.1.0
  • Reduced memory footprint by ~50% (still a ways to go)
  • improved iterator methods
  • 2D Indexing for CsvReader
  • on average ~20% faster parsing
  • CsvDictReader no longer in "beta"
  • CsvWriter no longer in "beta"
  • Python Shim (still needs some work)

2D indexing

The grid is row-major: reader[row, col] returns the cell at that position.

reader[0, 0]          # first cell
reader[2, 5]          # third row, sixth column

Iterating rows

for row in reader: yields row views. A row view shares the reader's parsed
cells (cheap to copy) and can be indexed by column.

for row in reader:
    print(row[0], row[1])  # cells by column

Row views are also available explicitly:

var row = reader.row(2)     # third row
print(len(row))             # number of columns in the row
print(repr(row))            # ['a', 'b', 'c']
print(row[0])               # first cell of row 2

DictCsvReader

Reads a CSV as rows of dicts keyed by the header row. All rows share one
refcounted header list, so rows are cheap to copy.

from mojo_csv import DictCsvReader
from std.pathlib import Path

def main() raises:
    var dr = DictCsvReader(
        in_csv: Path,
        delimiter: String = ",",
        quotation_mark: String = '"',
        num_threads: Int = 0,
    )

    for row in dr:
        print(row.get("name"), row.get("score"))
row.get(key : String) raises -> String # value for a header key
row.get_at(idx : Int) raises -> String # value by column position
row.keys() -> List[String] # header names (copy)
row.vals() -> List[String] # row values (copy)
row.col_count() -> Int

CsvWriter

Encodes cells per CSV rules (quotes doubled, fields quoted when they contain
the delimiter, quotes, or newlines). Fields that are already quoted are left
as-is. Threaded by default (uses logical cores - 2).

CsvWriter(
    frame: List[String],       # flat cells: row-major, col_count per row
    delimiter: String = ",",
    quotation_mark: String = '"',
    num_threads: Int = 0,
) raises

writer.write(
    out_csv: Path,
    col_count: Int,
    include_trailing_newline: Bool = False,
) raises
from mojo_csv import CsvWriter
from std.pathlib import Path

def main() raises:
    var cells: List[String] = ["name", "score", "alice", "95", "bob", "87"]
    var writer = CsvWriter(cells)
    writer.write(Path("out.csv"), col_count=2, include_trailing_newline=True)

Python interop

Build the compiled bindings, then use mojo_csv from Python:
(Currently this is slower the python's stdlib csv parser with the ffi translation)

python/mojo_csv_bind.mojo exposes a small CPython extension module
(python/mojo_csv_bind.so) built with mojo build --emit shared-lib:

import mojo_csv_bind as mojo_csv

# one-shot helpers (each parses the file)
cells = mojo_csv.parse_csv("data.csv")          # flat list[str]
rows = mojo_csv.row_count("data.csv")           # int
cols = mojo_csv.col_count("data.csv")           # int
cell = mojo_csv.cell("data.csv", 1, 2)          # reader[row, col]
headers = mojo_csv.headers("data.csv")          # list[str]
data = mojo_csv.dict_rows("data.csv")           # list[dict[str, str]]
rows = mojo_csv.write_csv(cells, cols, "out.csv")  # returns row count

# Reader type: parse once, then reuse the grid (recommended for repeated access)
reader = mojo_csv.Reader("data.csv")
reader = mojo_csv.Reader("data.csv", 8)         # optional thread count
reader.row_count()                              # int
reader.col_count()                              # int
reader.length()                                 # total cells
reader.cell(1, 2)                               # cell at row, col
reader.get_row(3)                               # list[str] for one row
reader.headers()                                # list[str]
reader.all_cells()                              # flat list[str]

Full Changelog: v1.6.3...1.7.0