Skip to content

Commit d7656af

Browse files
committed
add exmaples
1 parent 3375f41 commit d7656af

2 files changed

Lines changed: 123 additions & 2 deletions

File tree

README.md

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,84 @@ npm install csv-range
1717

1818
## Usage
1919

20-
To parse a remote CSV file from a URL:
20+
Parse a remote CSV file from a URL:
2121

2222
```typescript
2323
import { parseURL } from 'csv-range'
24+
const url = 'https://data.source.coop/severo/csv-papaparse-test-files/sample.csv'
2425
const rows = []
25-
for await (const { row } of parseURL('https://data.source.coop/severo/csv-papaparse-test-files/sample.csv')) {
26+
for await (const { row } of parseURL(url)) {
2627
rows.push(row)
2728
}
2829
console.log(rows)
2930
// Output: [ [ 'A', 'B', 'C' ], [ 'X', 'Y', 'Z' ] ]
3031
```
3132

33+
### Output format
34+
35+
The `parseURL` function yields an object for each row with the following properties:
36+
- `row`: array of strings with the values of the row.
37+
- `errors`: array of parsing errors found in the row.
38+
- `meta`: object with metadata about the parsing process.
39+
40+
The format is described on the doc pages: https://severo.github.io/csv-range/interfaces/ParseResult.html.
41+
42+
The `row` field might contain fewer or more columns than expected, depending on the CSV content. It can be an empty array for empty rows. It's up to the user to handle these cases. The library does not trim whitespace from values, and it does not convert types.
43+
44+
The `errors` field contains any parsing errors found in the row. It's an array of error messages, which can be useful for debugging. See the possible errors in the doc pages: https://severo.github.io/csv-range/interfaces/ParseError.html.
45+
46+
The `meta` field provides the `delimiter` and `newline` strings, detected automatically, or specified by the user. It also gives the number of characters of the line (as counted by JavaScript) and the corresponding number of bytes in the original CSV file (which may differ due to multi-byte characters) and byte offset in the file. These counts include the newline characters.
47+
48+
### Options
49+
50+
The `parseURL` function accepts an optional second argument with options.
51+
52+
It can contain options for fetching the CSV file: https://severo.github.io/csv-range/interfaces/FetchOptions.html, for guessing the delimiter and newline characters: https://severo.github.io/csv-range/interfaces/GuessOptions.html, and for parsing the CSV content: https://severo.github.io/csv-range/interfaces/ParseOptions.html.
53+
54+
55+
## Examples
56+
57+
Find some examples of usage below. You can also find them in the [examples](https://severo.github.io/csv-range/examples/) directory, and run them with `npm run examples`.
58+
59+
### Only the first 10 rows
60+
61+
As the library uses async iterators, it's easy to stop parsing after a certain number of rows:
62+
63+
```typescript
64+
import { parseURL } from 'csv-range'
65+
const url = 'https://data.source.coop/severo/csv-papaparse-test-files/verylong-sample.csv'
66+
const rows = []
67+
let count = 0
68+
for await (const { row } of parseURL(url)) {
69+
rows.push(row)
70+
count++
71+
if (count >= 10) {
72+
break
73+
}
74+
}
75+
console.log(rows)
76+
```
77+
78+
### Fetch a specific byte range
79+
80+
You can fetch only a specific byte range of the CSV file, to parse only a part of it. This is useful for large files.
81+
82+
```typescript
83+
import { parseURL } from 'csv-range'
84+
const url = 'https://data.source.coop/severo/csv-papaparse-test-files/verylong-sample.csv'
85+
const fetchOptions = {
86+
firstByte: 30_000,
87+
lastByte: 30_200
88+
}
89+
const rows = []
90+
for await (const { row } of parseURL(url, { fetch: fetchOptions })) {
91+
rows.push(row)
92+
}
93+
console.log(rows)
94+
```
95+
96+
Use the `result.meta.byteOffset` and `result.meta.byteCount` fields to know the exact byte range of each parsed row, and adjust your fetching strategy accordingly. See the [examples](https://severo.github.io/csv-range/examples/) for more details.
97+
3298
## Early version
3399

34100
This is an early version. The API may change completely:
@@ -37,6 +103,10 @@ This is an early version. The API may change completely:
37103
- from version 0.1.0 to 1.0.0, breaking changes will be introduced only in minor versions.
38104
- from version 1.0.0, breaking changes will be introduced only in major versions.
39105

106+
## Used by
107+
108+
This library is used by [source.coop](https://source.coop/severo/csv-papaparse-test-files/verylong-sample.csv) to preview the CSV files. More info in [csv-table](https://github.com/source-cooperative/csv-table/), which fetches ranges of the remote CSV to display the rows that are visible in the table. It also caches the fetched ranges to avoid re-fetching them when scrolling.
109+
40110
## Thanks
41111

42112
The code is heavily inspired by [Papaparse](https://www.papaparse.com/).

examples/examples.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,55 @@ describe('README examples', () => {
2121
)
2222
expect(rows).toEqual([['A', 'B', 'C'], ['X', 'Y', 'Z']])
2323
})
24+
25+
it('parses only the first 10 rows', async () => {
26+
const url = 'https://data.source.coop/severo/csv-papaparse-test-files/verylong-sample.csv'
27+
const rows = []
28+
let count = 0
29+
for await (const { row } of parseURL(url)) {
30+
rows.push(row)
31+
count++
32+
if (count >= 10) {
33+
break
34+
}
35+
}
36+
expect(rows.length).toBe(10)
37+
})
38+
39+
it('fetches a specific byte range', { timeout: 10_000 }, async () => {
40+
const url = 'https://data.source.coop/severo/csv-papaparse-test-files/verylong-sample.csv'
41+
const results = []
42+
for await (const result of parseURL(url, {
43+
firstByte: 30_000,
44+
lastByte: 30_250,
45+
})) {
46+
results.push(result)
47+
}
48+
// The first row might be incomplete, depending on how well the user picked the byte range
49+
expect(results[0]?.row).toEqual(['', 'ABC'])
50+
results.slice(1, 8).forEach((r) => {
51+
expect(r.row.length).toEqual(3)
52+
})
53+
// The last row might be incomplete, depending on how well the user picked the byte range
54+
expect(results[8]?.row).toEqual(['Lore'])
55+
56+
const first = results[1]
57+
const last = results[7]
58+
if (!first || !last) {
59+
throw new Error('Expected results to have more rows')
60+
}
61+
const options = {
62+
firstByte: first.meta.byteOffset,
63+
lastByte: last.meta.byteOffset + last.meta.byteCount - 1,
64+
}
65+
// Re-fetch the same range again to verify byte offsets and counts are correct
66+
const rows = []
67+
for await (const { row } of parseURL(url, options)) {
68+
rows.push(row)
69+
}
70+
expect(rows).toEqual([
71+
...results.slice(1, 8).map(r => r.row),
72+
[''], // When the last row contains a line ending, an extra empty row is produced
73+
])
74+
})
2475
})

0 commit comments

Comments
 (0)