Need to read and parse a CSV file in Deno? Find out in this tutorial.
Reading and parsing a CSV file can be done easily in Deno. First, you need to load the file contents using built-in functions such as Deno.readTextFile
or Deno.open
. For parsing the file contents, Deno has a module named csv
as a part of std
modules. Below are the usage examples which include how to create custom parse functions, customize the column names, set the separator, as well as using BufReader
.
Using Deno std
csv Module
The function of csv
module you need to use is either parse
or readMatrix
.
Using parse
First, import and re-export the module in deps.ts
. That makes it easier for us to manage the dependency version instead of directly importing the module in each file that uses the function.
import {
parse as parseCsv
} from 'https://deno.land/std@0.82.0/encoding/csv.ts';
export { parseCsv };
For this tutorial, let's assume we have a csv file named items.csv
with the following contents.
Name,Quantity,Price
One,100,1000
Two,10,5000
Three,20,3000
We are going to use parse
function which is shown below.
async function parse(
input: string | BufReader,
opt: ParseOptions = {
skipFirstRow: false,
},
)
You are required to pass an input as the first parameter which can be a string or a BufReader
. To get the contents of a file as a string, you can use Deno.readTextFile
. Because Deno needs to read a file, you need to add --allow read
flag while running deno run
command. The second parameter is ParseOptions
object. ParseOptions
has the following fields.
skipFirstRow?: boolean
: Whether to exclude first row as data. If the value istrue
andcolumns
option is provided, the first line will be skipped. If the value istrue
butcolumns
option is not provided, the first line will be used as the header.columns?: string[] | ColumnOptions[]
: Allows you to define the list of column names and create a custom parse function for the column.parse?: (input: unknown) => unknown
: Custom parse functions for all rows.separator?: string
: Character that separates values. Defaults to,
.comment?: string
: Character that starts a comment. Defaults to#
.trimLeadingSpace?: boolean
: Whether to trim the leading space of the value. Defaults tofalse
.lazyQuotes?: boolean
: Whether to allow unquoted quote in a quoted field or non double quoted quotes in quoted field. Defaults tofalse
.fieldsPerRecord?: number
: The row number to be used as number of column reference. If this value is set, it will check the number of fields on each row.
The ColumnOptions
type has two fields:
name: string
: The column name.parse?: (input: string) => unknown
: Parse function for the column.
By default, if you don't pass the second argument, Deno will use the default value which sets skipFirstRow
to false
. Below is a basic example that reads the file contents as a string first.
import { parseCsv } from './deps.ts';
const content = await parseCsv(await Deno.readTextFile('items.csv'));
console.log(content);
Output:
[
[ "Name", "Quantity", "Price" ],
[ "One", "100", "1000" ],
[ "Two", "10", "5000" ],
[ "Three", "20", "3000" ]
]
From the above output, you can see that the first row is treated as data by default. To make the first row treated as the header, you can set skipFirstRow
option to true
. It also changes the return type to a list of objects.
import { parseCsv } from './deps.ts';
const content = await parseCsv(await Deno.readTextFile('items.csv'), {
skipFirstRow: true,
});
console.log(content);
Output:
[
{ Name: "One", Quantity: "100", Price: "1000" },
{ Name: "Two", Quantity: "10", Price: "5000" },
{ Name: "Three", Quantity: "20", Price: "3000" }
]
Set Column Names and Parse Function
If you want to use different column names, you can pass columns
option with an array of strings as the value to be used as column names.
import { parseCsv } from './deps.ts';
const content = await parseCsv(await Deno.readTextFile('items.csv'), {
skipFirstRow: true,
columns: ['item', 'quantity.', 'pricePerItem'],
});
console.log(content);
Output:
[
{ item: "One", "quantity.": "100", pricePerItem: "1000" },
{ item: "Two", "quantity.": "10", pricePerItem: "5000" },
{ item: "Three", "quantity.": "20", pricePerItem: "3000" }
]
If you need to use a custom parse function for certain columns, you can pass an array of ColumnOptions
objects as columns
option instead.
import { parseCsv } from './deps.ts';
const content = await parseCsv(await Deno.readTextFile('items.csv'), {
skipFirstRow: true,
columns: [
{ name: 'item' },
{ name: 'quantity' },
{
name: 'pricePerItem',
parse: (e: string): string => {
return `$${e}`;
},
},
],
});
console.log(content);
Output:
[
{ item: "One", "quantity.": "100", pricePerItem: "1000" },
{ item: "Two", "quantity.": "10", pricePerItem: "5000" },
{ item: "Three", "quantity.": "20", pricePerItem: "3000" }
]
The above examples always return the same number of columns according to the csv file. If you define columns
option with a different number of columns, you will get the following error.
error: Uncaught (in promise) Error number of fields line:1
However, it provides a way to return custom data for each row. You can pass a function as parse
option. The return value of the function can be any type. If you choose to return an object, the number of columns doesn't have to be the same as the number of columns in the csv file.
import { parseCsv } from './deps.ts';
const content = await parseCsv(await Deno.readTextFile('items.csv'), {
skipFirstRow: true,
parse: (e: any): unknown => {
return { item: e.Name, quantity: e.Quantity, pricePerItem: `$${e.Price.toLocaleString('en-US')}`, isExpensive: e.Price >= 3000 };
},
});
console.log(content);
[
{ item: "One", quantity: "100", pricePerItem: "$1000", isExpensive: false },
{ item: "Two", quantity: "10", pricePerItem: "$5000", isExpensive: true },
{ item: "Three", quantity: "20", pricePerItem: "$3000", isExpensive: true }
]
Set Separator
The default separator is comma (,). You can change it by passing separator
option.
const content = await parseCsv(await Deno.readTextFile('items2.csv'), { separator: ';' });
Validate Number of Columns
The parser can also validate the number of columns on each row. To enable the validation, you need to pass fieldsPerRecord
option with the number of row to be used as the reference.
const content = await parseCsv(await Deno.readTextFile('items2.csv'), { fieldsPerRecord: 0 });
If there's a row with a different number of columns, it will throw an error.
Check file:///home/ivan/Projects/deno/src/examples/csv_read.ts
error: Uncaught (in promise) Error: record on line 4: wrong number of fields
throw new ParseError(lineIndex, lineIndex, null, ERR_FIELD_COUNT);
^
at readMatrix (csv.ts:325:15)
at async parse (csv.ts:402:9)
at async csv_read.ts:12:17
Using BufReader
If the file is very big, it's recommended to pass a BufReader
instead of reading the entire file as a string. To create a BufReader
from a csv file, you can get an instance of File
by using Deno.open
. Then pass the FileBufReader
const file = await Deno.open('items.csv');
const content = await parseCsv(new BufReader(file));
Using readMatrix
Another function you can use to parse a csv file is readMatrix
function readMatrix(
reader: BufReader,
opt: ReadOptions = {
separator: ",",
trimLeadingSpace: false,
lazyQuotes: false,
},
)
The function returns string[][]
. You have to pass the input as BufReader
as it doesn't accept string. The second parameter is an object of type ReadOptions
which has the following fields.
separator?: string
: Character that separates values. Defaults to,
.comment?: string
: Character that starts a comment. Defaults to#
.trimLeadingSpace?: boolean
: Whether to trim the leading space of the value. Defaults tofalse
.lazyQuotes?: boolean
: Whether to allow unquoted quote in a quoted field or non double quoted quotes in quoted field. Defaults tofalse
.fieldsPerRecord?: number
: The row number to be used as number of column reference. If this value is set, it will check the number of fields on each row.
const file = await Deno.open('items.csv');
const content = await readMatrix(new BufReader(file));
Because readMatrix
doesn't have as many options as parse
and doesn't allow to have return type other than string[][]
, using parse
is a more popular and recommended choice.
That's how to parse a csv file in Deno.