Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# v3.3.0

* [FIXED] First row of CSV is removed when headers array is provided [#252](https://github.com/C2FO/fast-csv/issues/252)

# v3.2.0

* [FIXED] Invalid row index doesn't reflect original row count [#130](https://github.com/C2FO/fast-csv/issues/130) [#266](https://github.com/C2FO/fast-csv/pull/266) - [@chrwnsk](https://github.com/chrwnsk)
Expand Down
205 changes: 203 additions & 2 deletions docs/formatting.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
* [Alternate `rowDelimiter`](#examples-alternate-row-delimiter)
* [Alternate `quote`](#examples-alternate-quote)
* [Alternate `escape`](#examples-alternate-escape)
* [Headers](#examples-headers)
* Auto Discovery
* [Object Rows](#headers-auto-discover-object)
* [Hash Array Rows](#headers-auto-discover-hash-array)
* Provide Headers
* [Array Rows](#headers-provided-array)
* [Hash Array Rows](#headers-provided-hash-array)
* [Object Rows - Reorder Columns](#headers-provided-object)
* [Object Rows - Remove Columns](#headers-provided-object-remove-column)
* [`quoteColumns`](#examples-quote-columns)
* [`quoteHeaders`](#examples-quote-headers)
* [Transforming Rows](#examples-transforming)
Expand All @@ -31,9 +40,12 @@
* `includeEndRowDelimiter: {boolean} = false`: Set to `true` to include a row delimiter at the end of the csv.
* `headers: {null|boolean|string[]} = null`:
* If true then the headers will be auto detected from the first row.
* If the row is an object then the keys will be used.
* If the row is an array of two element arrays (`[ ['header', 'column'], ['header2', 'column2'] ]`) then the first element in each array will be used.
* If the row is a one-dimensional array then headers is a no-op
* If the row is an object then the keys will be used.
* If the row is an array of two element arrays (`[ ['header', 'column'], ['header2', 'column2'] ]`) then the first element in each array will be used.
* If there is not a headers row and you want to provide one then set to a `string[]`
* **NOTE** If the row is an object the headers must match fields in the object, otherwise you will end up with empty fields
* **NOTE** If there are more headers than columns then additional empty columns will be added
* `quoteColumns: {boolean|boolean[]|{[string]: boolean} = false`
* If `true` then columns and headers will be quoted (unless `quoteHeaders` is specified).
* If it is an object then each key that has a true value will be quoted ((unless `quoteHeaders` is specified)
Expand Down Expand Up @@ -331,6 +343,195 @@ Expected output
"'"a2'"","'"b2'""
```

<a name="examples-headers"></a>

### Headers

#### Auto Discovery

`fast-csv` will auto-discover headers when the `headers` option is set to `true`.

**NOTE** When working is one-dimensional array rows (e.g. `[ 'a', 'b', 'c' ]`) this is a no-op.

<a name="headers-auto-discover-object"></a>
[`examples/formatting/headers_auto_discovery_object.example.js`](../examples/formatting/headers_auto_discovery_object.example.js)

In this example the headers are auto-discovered from the objects passed in.

```js
const csvStream = csv.format({ headers: true});

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write({ header1: 'value1a', header2: 'value1b' });
csvStream.write({ header1: 'value2a', header2: 'value2b' });
csvStream.write({ header1: 'value3a', header2: 'value3b' });
csvStream.write({ header1: 'value4a', header2: 'value4b' });
csvStream.end();
```

Expected Output:
```
header1,header2
value1a,value1b
value2a,value2b
value3a,value3b
value4a,value4b
```

<a name="headers-auto-discover-hash-array"></a>
[`examples/formatting/headers_auto_discovery_hash_array.example.js`](../examples/formatting/headers_auto_discovery_hash_array.example.js)

In this example the headers are auto-discovered from the hash arrays passed in.

```js
const csvStream = csv.format({ headers: true });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write([ [ 'header1', 'value1a' ], [ 'header2', 'value1b' ] ]);
csvStream.write([ [ 'header1', 'value2a' ], [ 'header2', 'value2b' ] ]);
csvStream.write([ [ 'header1', 'value3a' ], [ 'header2', 'value3b' ] ]);
csvStream.write([ [ 'header1', 'value4a' ], [ 'header2', 'value4b' ] ]);
csvStream.end();
```

Expected Output:
```
header1,header2
value1a,value1b
value2a,value2b
value3a,value3b
value4a,value4b
```

### Provided Headers

You can also provide a set of `headers` by providing an array. This allows you to

* Reorder and/or exclude columns when working when object rows.
* Rename and/or exclude columns when working with hash array rows.
* Specify headers or remove columns when working with array rows.

**NOTE** When working with objects the header names should match keys. The headers option allows you to specify column order.

<a name="headers-provided-array"></a>
[`examples/formatting/headers_provided_array.example.js`](../examples/formatting/headers_provided_array.example.js)

In this example a custom set of headers is provided for rows that are arrays.

```js
const csvStream = csv.format({ headers: [ 'header1', 'header2' ] });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write([ 'value1a', 'value1b' ]);
csvStream.write([ 'value2a', 'value2b' ]);
csvStream.write([ 'value3a', 'value3b' ]);
csvStream.write([ 'value4a', 'value4b' ]);
csvStream.end();
```

Expected Output:
```
header1,header2
value1a,value1b
value2a,value2b
value3a,value3b
value4a,value4b
```

<a name="headers-provided-hash-array"></a>
[`examples/formatting/headers_provided_hash_array.example.js`](../examples/formatting/headers_provided_hash_array.example.js)

In this example the headers are overridden with a custom set of headers

```js
const csvStream = csv.format({ headers: [ 'header1', 'header2' ] });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write([ [ 'h1', 'value1a' ], [ 'h2', 'value1b' ] ]);
csvStream.write([ [ 'h1', 'value2a' ], [ 'h2', 'value2b' ] ]);
csvStream.write([ [ 'h1', 'value3a' ], [ 'h2', 'value3b' ] ]);
csvStream.write([ [ 'h1', 'value4a' ], [ 'h2', 'value4b' ] ]);
csvStream.end();
```

Expected Output:
```
header1,header2
value1a,value1b
value2a,value2b
value3a,value3b
value4a,value4b
```

<a name="headers-provided-object"></a>
[`examples/formatting/headers_provided_object.example.js`](../examples/formatting/headers_provided_object.example.js)

In this example the columns are reordered.

```js
const csvStream = csv.format({ headers: [ 'header2', 'header1' ] });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write({ header1: 'value1a', header2: 'value1b' });
csvStream.write({ header1: 'value2a', header2: 'value2b' });
csvStream.write({ header1: 'value3a', header2: 'value3b' });
csvStream.write({ header1: 'value4a', header2: 'value4b' });
csvStream.end();

```

Expected Output:
```
header2,header1
value1b,value1a
value2b,value2a
value3b,value3a
value4b,value4a
```

<a name="headers-provided-object-remove-column"></a>
[`examples/formatting/headers_provided_object_remove_column.example.js`](../examples/formatting/headers_provided_object_remove_column.example.js)

In this example the one of the columns is removed.

```js
const csvStream = csv.format({ headers: [ 'header2' ] });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write({ header1: 'value1a', header2: 'value1b' });
csvStream.write({ header1: 'value2a', header2: 'value2b' });
csvStream.write({ header1: 'value3a', header2: 'value3b' });
csvStream.write({ header1: 'value4a', header2: 'value4b' });
csvStream.end();
```

Expected Output:
```
header2
value1b
value2b
value3b
value4b
```

<a name="examples-quote-columns"></a>
### `quoteColumns`

Expand Down
13 changes: 13 additions & 0 deletions examples/formatting/headers_auto_discovery_hash_array.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const csv = require('../../');

const csvStream = csv.format({ headers: true });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write([ [ 'header1', 'value1a' ], [ 'header2', 'value1b' ] ]);
csvStream.write([ [ 'header1', 'value2a' ], [ 'header2', 'value2b' ] ]);
csvStream.write([ [ 'header1', 'value3a' ], [ 'header2', 'value3b' ] ]);
csvStream.write([ [ 'header1', 'value4a' ], [ 'header2', 'value4b' ] ]);
csvStream.end();
13 changes: 13 additions & 0 deletions examples/formatting/headers_auto_discovery_object.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const csv = require('../../');

const csvStream = csv.format({ headers: true });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write({ header1: 'value1a', header2: 'value1b' });
csvStream.write({ header1: 'value2a', header2: 'value2b' });
csvStream.write({ header1: 'value3a', header2: 'value3b' });
csvStream.write({ header1: 'value4a', header2: 'value4b' });
csvStream.end();
13 changes: 13 additions & 0 deletions examples/formatting/headers_provided_array.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const csv = require('../../');

const csvStream = csv.format({ headers: [ 'header1', 'header2' ] });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write([ 'value1a', 'value1b' ]);
csvStream.write([ 'value2a', 'value2b' ]);
csvStream.write([ 'value3a', 'value3b' ]);
csvStream.write([ 'value4a', 'value4b' ]);
csvStream.end();
13 changes: 13 additions & 0 deletions examples/formatting/headers_provided_hash_array.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const csv = require('../../');

const csvStream = csv.format({ headers: [ 'header1', 'header2' ] });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write([ [ 'h1', 'value1a' ], [ 'h2', 'value1b' ] ]);
csvStream.write([ [ 'h1', 'value2a' ], [ 'h2', 'value2b' ] ]);
csvStream.write([ [ 'h1', 'value3a' ], [ 'h2', 'value3b' ] ]);
csvStream.write([ [ 'h1', 'value4a' ], [ 'h2', 'value4b' ] ]);
csvStream.end();
13 changes: 13 additions & 0 deletions examples/formatting/headers_provided_object.example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const csv = require('../../');

const csvStream = csv.format({ headers: [ 'header2', 'header1' ] });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write({ header1: 'value1a', header2: 'value1b' });
csvStream.write({ header1: 'value2a', header2: 'value2b' });
csvStream.write({ header1: 'value3a', header2: 'value3b' });
csvStream.write({ header1: 'value4a', header2: 'value4b' });
csvStream.end();
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const csv = require('../../');

const csvStream = csv.format({ headers: [ 'header2' ] });

csvStream
.pipe(process.stdout)
.on('end', process.exit);

csvStream.write({ header1: 'value1a', header2: 'value1b' });
csvStream.write({ header1: 'value2a', header2: 'value2b' });
csvStream.write({ header1: 'value3a', header2: 'value3b' });
csvStream.write({ header1: 'value4a', header2: 'value4b' });
csvStream.end();
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fast-csv",
"version": "3.2.0",
"version": "3.3.0",
"description": "CSV parser and writer",
"main": "./build/src/index.js",
"types": "./build/src/index.d.ts",
Expand Down
4 changes: 2 additions & 2 deletions src/formatter/FormatterOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class FormatterOptions {

public readonly transform: RowTransformFunction | null = null;

public readonly hasProvidedHeaders: boolean;
public readonly shouldWriteHeaders: boolean;

public readonly escapedQuote: string;

Expand All @@ -54,7 +54,7 @@ export class FormatterOptions {
this.escape = this.quote;
}
}
this.hasProvidedHeaders = !!this.headers;
this.shouldWriteHeaders = !!this.headers;
this.headers = Array.isArray(this.headers) ? this.headers : null;
this.escapedQuote = `${this.escape}${this.quote}`;
}
Expand Down
Loading