Skip to content

Commit 84ca8e2

Browse files
authored
Merge pull request #307 from C2FO/v3.7.0-rc
v3.7.0
2 parents e0d945b + b2db63c commit 84ca8e2

28 files changed

+731
-167
lines changed

.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ module.exports = {
4848
"prettier/prettier": "error",
4949
"indent": [
5050
"error",
51-
4
51+
4,
52+
{ "SwitchCase": 1 }
5253
],
5354
"no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "WithStatement"],
5455
"object-curly-spacing": ["error", "always"],

History.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# v3.7.0
2+
3+
* [ADDED] Ability to Transform Header [#287](https://github.com/C2FO/fast-csv/issues/287)
4+
* [ADDED] Example require and import to README [#301](https://github.com/C2FO/fast-csv/issues/301)
5+
* [ADDED] Added new formatting option `alwaysWriteHeaders` to always write headers even if no rows are provided [#300](https://github.com/C2FO/fast-csv/issues/300)
6+
* [ADDED] Appending to csv example and docs [#272](https://github.com/C2FO/fast-csv/issues/300)
7+
* [FIXED] Issue with duplicate headers causing dataloss, duplicate headers will can an error to be emitted. [#276](https://github.com/C2FO/fast-csv/issues/272)
8+
* [FIXED] Issue where an error thrown while processing rows causes stream continue to parse, causing duplicate writes or swallowed exceptions.
9+
10+
111
# v3.6.0
212

313
* [ADDED] `maxRows` option to limit the number of rows parsed. [#275](https://github.com/C2FO/fast-csv/issues/275) [#277](https://github.com/C2FO/fast-csv/pull/277) - [@cbrittingham](https://github.com/cbrittingham)

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,20 @@ Fast-csv is library for parsing and formatting csvs or any other delimited value
1111

1212
`npm install -S fast-csv`
1313

14+
## Usage
15+
16+
To use `fast-csv` in `javascript` you can require the module/
17+
18+
```js
19+
const csv = require('fast-csv');
20+
```
21+
22+
To import with typescript
23+
24+
```typescript
25+
import * as csv from 'fast-csv';
26+
```
27+
1428
## Documentation
1529

1630
* [Parsing Docs](./docs/parsing.md)
@@ -55,3 +69,4 @@ MIT <https://github.com/C2FO/fast-csv/raw/master/LICENSE>
5569
* Website: <http://c2fo.com>
5670
* Twitter: [http://twitter.com/c2fo](http://twitter.com/c2fo) - 877.465.4045
5771

72+

benchmark/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const benchmarkFastCsv = type => num => {
2828
const file = path.resolve(__dirname, `./assets/${num}.${type}.csv`);
2929
const stream = fs
3030
.createReadStream(file)
31-
.pipe(fastCsv.parse({ headers: true, maxRows: 10 }))
31+
.pipe(fastCsv.parse({ headers: true }))
3232
.transform(data => {
3333
const ret = {};
3434
['first_name', 'last_name', 'email_address'].forEach(prop => {

docs/formatting.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* [`quoteColumns`](#examples-quote-columns)
2727
* [`quoteHeaders`](#examples-quote-headers)
2828
* [Transforming Rows](#examples-transforming)
29+
* [Appending To A CSV](#examples-appending)
2930

3031
<a name="options"></a>
3132
## Options
@@ -52,6 +53,8 @@
5253
* If there is not a headers row and you want to provide one then set to a `string[]`
5354
* **NOTE** If the row is an object the headers must match fields in the object, otherwise you will end up with empty fields
5455
* **NOTE** If there are more headers than columns then additional empty columns will be added
56+
* `alwaysWriteHeaders: {boolean} = false`: Set to true if you always want headers written, even if no rows are written.
57+
* **NOTE** This will throw an error if headers are not specified as an array.
5558
* `quoteColumns: {boolean|boolean[]|{[string]: boolean} = false`
5659
* If `true` then columns and headers will be quoted (unless `quoteHeaders` is specified).
5760
* If it is an object then each key that has a true value will be quoted ((unless `quoteHeaders` is specified)
@@ -836,4 +839,88 @@ VALUE1A,VALUE2A
836839
VALUE1A,VALUE2A
837840
VALUE1A,VALUE2A
838841
VALUE1A,VALUE2A
842+
```
843+
844+
<a name="examples-appending"></a>
845+
### Appending To A CSV
846+
847+
[`examples/formatting/append.example.js`](../examples/formatting/append.example.js)
848+
849+
In this example a new csv is created then appended to.
850+
851+
```javascript
852+
const path = require('path');
853+
const fs = require('fs');
854+
855+
const write = (filestream, rows, options) => {
856+
return new Promise((res, rej) => {
857+
csv.writeToStream(filestream, rows, options)
858+
.on('error', err => rej(err))
859+
.on('finish', () => res());
860+
});
861+
};
862+
863+
// create a new csv
864+
const createCsv = (filePath, rows) => {
865+
const csvFile = fs.createWriteStream(filePath);
866+
return write(csvFile, rows, { headers: true, includeEndRowDelimiter: true });
867+
};
868+
869+
// append the rows to the csv
870+
const appendToCsv = (filePath, rows = []) => {
871+
const csvFile = fs.createWriteStream(filePath, { flags: 'a' });
872+
// notice how headers are set to false
873+
return write(csvFile, rows, { headers: false });
874+
};
875+
876+
// read the file
877+
const readFile = filePath => {
878+
return new Promise((res, rej) => {
879+
fs.readFile(filePath, (err, contents) => {
880+
if (err) {
881+
return rej(err);
882+
}
883+
return res(contents);
884+
});
885+
});
886+
};
887+
888+
const csvFilePath = path.resolve(__dirname, 'tmp', 'append.csv');
889+
890+
// 1. create the csv
891+
createCsv(csvFilePath, [
892+
{ a: 'a1', b: 'b1', c: 'c1' },
893+
{ a: 'a2', b: 'b2', c: 'c2' },
894+
{ a: 'a3', b: 'b3', c: 'c3' },
895+
])
896+
.then(() => {
897+
// 2. append to the csv
898+
return appendToCsv(csvFilePath, [
899+
{ a: 'a4', b: 'b4', c: 'c4' },
900+
{ a: 'a5', b: 'b5', c: 'c5' },
901+
{ a: 'a6', b: 'b6', c: 'c6' },
902+
]);
903+
})
904+
.then(() => readFile(csvFilePath))
905+
.then(contents => {
906+
console.log(`${contents}`);
907+
})
908+
.catch(err => {
909+
console.error(err.stack);
910+
process.exit(1);
911+
});
912+
913+
914+
```
915+
916+
Expected output
917+
918+
```
919+
a,b,c
920+
a1,b1,c1
921+
a2,b2,c2
922+
a3,b3,c3
923+
a4,b4,c4
924+
a5,b5,c5
925+
a6,b6,c6
839926
```

docs/parsing.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* [First Row As Headers](#csv-parse-first-row-as-headers)
1414
* [Custom Headers](#csv-parse-custom-headers)
1515
* [Renaming Headers](#csv-parse-renaming-headers)
16+
* [Transforming Headers](#csv-parse-transforming-headers)
1617
* [Skipping Columns](#csv-parse-skipping-columns)
1718
* [Ignoring Empty Rows](#csv-parse-ignoring-empty-rows)
1819
* [Transforming Rows](#csv-parse-transforming)
@@ -31,12 +32,16 @@
3132
* `"first,name",last name`
3233
* `escape: {string} = '"'`: The character to used tp escape quotes inside of a quoted field.
3334
* `i.e`: `First,"Name"' => '"First,""Name"""`
34-
* `headers: {boolean|string[]} = false`:
35+
* `headers: {boolean|string[]|(string[]) => string[])} = false`:
3536
* If you want the first row to be treated as headers then set to `true`
3637
* If there is not a headers row and you want to provide one then set to a `string[]`
3738
* If you wish to discard the first row and use your own headers set to a `string[]` and set the `renameHeaders` option to `true`
38-
* `renameHeaders: {boolean} = false`: If you want the first line of the file to be removed and replaced by the one provided in the `headers` option.
39+
* If you wish to transform the headers you can provide a transform function.
40+
* **NOTE** This will always rename the headers
41+
* **NOTE** If headers either parsed, provided or transformed are NOT unique, then an error will be emitted and the stream will stop parsing.
42+
* `renameHeaders: {boolean} = false`: If you want the first line of the file to be removed and replaced by the one provided in the `headers` option.
3943
* **NOTE** This option should only be used if the `headers` option is a `string[]`
44+
* **NOTE** If the `headers` option is a function then this option is always set to true.
4045
* `ignoreEmpty: {boolean} = false`: If you wish to ignore empty rows.
4146
* **NOTE** this will discard columns that are all white space or delimiters.
4247
* `comment: {string} = null`: If your CSV contains comments you can use this option to ignore lines that begin with the specified character (e.g. `#`).
@@ -337,6 +342,39 @@ Expected output
337342
Parsed 2 rows
338343
```
339344

345+
<a name="csv-parse-transforming-headers"></a>
346+
### Transforming Headers
347+
348+
If the CSV contains a header row but you want transform the headers you can provide a function to the `headers` option.
349+
350+
[`examples/parsing/rename_headers.example.js`](../examples/parsing/rename_headers.example.js)
351+
352+
```javascript
353+
const { EOL } = require('os');
354+
355+
const CSV_STRING = ['header1,header2', 'a1,b1', 'a2,b2'].join(EOL);
356+
357+
const stream = csv
358+
.parse({
359+
headers: headers => headers.map(h => h.toUpperCase()),
360+
})
361+
.on('error', error => console.error(error))
362+
.on('data', row => console.log(row))
363+
.on('end', rowCount => console.log(`Parsed ${rowCount} rows`));
364+
365+
stream.write(CSV_STRING);
366+
stream.end();
367+
368+
```
369+
370+
Expected output
371+
372+
```
373+
{ HEADER1: 'a1', HEADER2: 'b1' }
374+
{ HEADER1: 'a2', HEADER2: 'b2' }
375+
Parsed 2 rows
376+
```
377+
340378
<a name="csv-parse-skipping-columns"></a>
341379
### Skipping Columns
342380

@@ -711,3 +749,4 @@ Parsed 4 rows
711749
```
712750

713751

752+
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const path = require('path');
2+
const fs = require('fs');
3+
const csv = require('../..');
4+
5+
const write = (filestream, rows, options) => {
6+
return new Promise((res, rej) => {
7+
csv.writeToStream(filestream, rows, options)
8+
.on('error', err => rej(err))
9+
.on('finish', () => res());
10+
});
11+
};
12+
13+
// create a new csv
14+
const createCsv = (filePath, rows) => {
15+
const csvFile = fs.createWriteStream(filePath);
16+
return write(csvFile, rows, { headers: true, includeEndRowDelimiter: true });
17+
};
18+
19+
// append the rows to the csv
20+
const appendToCsv = (filePath, rows = []) => {
21+
const csvFile = fs.createWriteStream(filePath, { flags: 'a' });
22+
// notice how headers are set to false
23+
return write(csvFile, rows, { headers: false });
24+
};
25+
26+
// read the file
27+
const readFile = filePath => {
28+
return new Promise((res, rej) => {
29+
fs.readFile(filePath, (err, contents) => {
30+
if (err) {
31+
return rej(err);
32+
}
33+
return res(contents);
34+
});
35+
});
36+
};
37+
38+
const csvFilePath = path.resolve(__dirname, 'tmp', 'append.csv');
39+
40+
// 1. create the csv
41+
createCsv(csvFilePath, [
42+
{ a: 'a1', b: 'b1', c: 'c1' },
43+
{ a: 'a2', b: 'b2', c: 'c2' },
44+
{ a: 'a3', b: 'b3', c: 'c3' },
45+
])
46+
.then(() => {
47+
// 2. append to the csv
48+
return appendToCsv(csvFilePath, [
49+
{ a: 'a4', b: 'b4', c: 'c4' },
50+
{ a: 'a5', b: 'b5', c: 'c5' },
51+
{ a: 'a6', b: 'b6', c: 'c6' },
52+
]);
53+
})
54+
.then(() => readFile(csvFilePath))
55+
.then(contents => {
56+
console.log(`${contents}`);
57+
})
58+
.catch(err => {
59+
console.error(err.stack);
60+
process.exit(1);
61+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const { EOL } = require('os');
2+
const csv = require('../../');
3+
4+
const CSV_STRING = ['header1,header2', 'a1,b1', 'a2,b2'].join(EOL);
5+
6+
const stream = csv
7+
.parse({
8+
headers: headers => headers.map(h => h.toUpperCase()),
9+
})
10+
.on('error', error => console.error(error))
11+
.on('data', row => console.log(row))
12+
.on('end', rowCount => console.log(`Parsed ${rowCount} rows`));
13+
14+
stream.write(CSV_STRING);
15+
stream.end();

package-lock.json

Lines changed: 29 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)