Comparing version 3.2.0 to 3.3.0
@@ -12,3 +12,3 @@ import { FormatterOptions } from '../FormatterOptions'; | ||
private headers; | ||
private parsedHeaders; | ||
private shouldWriteHeaders; | ||
private hasWrittenHeaders; | ||
@@ -15,0 +15,0 @@ private rowCount; |
@@ -12,5 +12,5 @@ "use strict"; | ||
this.headers = formatterOptions.headers; | ||
this.parsedHeaders = formatterOptions.hasProvidedHeaders && Array.isArray(formatterOptions.headers); | ||
this.hasWrittenHeaders = !formatterOptions.hasProvidedHeaders; | ||
if (this.parsedHeaders && this.headers !== null) { | ||
this.shouldWriteHeaders = formatterOptions.shouldWriteHeaders; | ||
this.hasWrittenHeaders = false; | ||
if (this.headers !== null) { | ||
this.fieldFormatter.headers = this.headers; | ||
@@ -74,4 +74,5 @@ } | ||
const { shouldFormatColumns, headers } = this.checkHeaders(transformedRow); | ||
if (headers) { | ||
if (this.shouldWriteHeaders && headers && !this.hasWrittenHeaders) { | ||
rows.push(this.formatColumns(headers, true)); | ||
this.hasWrittenHeaders = true; | ||
} | ||
@@ -89,25 +90,34 @@ if (shouldFormatColumns) { | ||
checkHeaders(row) { | ||
if (!this.parsedHeaders) { | ||
this.parsedHeaders = true; | ||
this.headers = RowFormatter.gatherHeaders(row); | ||
this.fieldFormatter.headers = this.headers; | ||
if (this.headers) { | ||
// either the headers were provided by the user or we have already gathered them. | ||
return { shouldFormatColumns: true, headers: this.headers }; | ||
} | ||
if (this.hasWrittenHeaders) { | ||
const headers = RowFormatter.gatherHeaders(row); | ||
this.headers = headers; | ||
this.fieldFormatter.headers = headers; | ||
if (!this.shouldWriteHeaders) { | ||
// if we are not supposed to write the headers then | ||
// alwyas format the columns | ||
return { shouldFormatColumns: true, headers: null }; | ||
} | ||
this.hasWrittenHeaders = true; | ||
const shouldFormatColumns = RowFormatter.isHashArray(row) || !Array.isArray(row); | ||
return { shouldFormatColumns, headers: this.headers }; | ||
// if the row is equal to headers dont format | ||
return { shouldFormatColumns: !lodash_1.isEqual(headers, row), headers }; | ||
} | ||
gatherColumns(row) { | ||
if (this.headers === null) { | ||
throw new Error('Headers is currently null'); | ||
} | ||
if (!Array.isArray(row)) { | ||
if (this.headers === null) { | ||
throw new Error('Headers is currently null'); | ||
} | ||
return this.headers.map((header) => row[header]); | ||
} | ||
if (RowFormatter.isHashArray(row)) { | ||
return row.map((col) => col[1]); | ||
return this.headers.map((header, i) => { | ||
const col = row[i]; | ||
if (col) { | ||
return col[1]; | ||
} | ||
return ''; | ||
}); | ||
} | ||
return row; | ||
return this.headers.map((header, i) => row[i]); | ||
} | ||
@@ -114,0 +124,0 @@ callTransformer(row, cb) { |
@@ -29,3 +29,3 @@ import { RowTransformFunction } from './types'; | ||
readonly transform: RowTransformFunction | null; | ||
readonly hasProvidedHeaders: boolean; | ||
readonly shouldWriteHeaders: boolean; | ||
readonly escapedQuote: string; | ||
@@ -32,0 +32,0 @@ constructor(opts?: FormatterOptionsArgs); |
@@ -24,3 +24,3 @@ "use strict"; | ||
} | ||
this.hasProvidedHeaders = !!this.headers; | ||
this.shouldWriteHeaders = !!this.headers; | ||
this.headers = Array.isArray(this.headers) ? this.headers : null; | ||
@@ -27,0 +27,0 @@ this.escapedQuote = `${this.escape}${this.quote}`; |
@@ -17,2 +17,11 @@ # Formatting | ||
* [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) | ||
@@ -35,5 +44,8 @@ * [`quoteHeaders`](#examples-quote-headers) | ||
* 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` | ||
@@ -336,2 +348,191 @@ * If `true` then columns and headers will be quoted (unless `quoteHeaders` is specified). | ||
<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> | ||
@@ -338,0 +539,0 @@ ### `quoteColumns` |
@@ -0,1 +1,5 @@ | ||
# 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 | ||
@@ -2,0 +6,0 @@ |
{ | ||
"name": "fast-csv", | ||
"version": "3.2.0", | ||
"version": "3.3.0", | ||
"description": "CSV parser and writer", | ||
@@ -5,0 +5,0 @@ "main": "./build/src/index.js", |
@@ -1,2 +0,2 @@ | ||
import { isFunction } from 'lodash'; | ||
import { isFunction, isEqual } from 'lodash'; | ||
import { FormatterOptions } from '../FormatterOptions'; | ||
@@ -60,3 +60,3 @@ import FieldFormatter from './FieldFormatter'; | ||
private parsedHeaders: boolean; | ||
private shouldWriteHeaders: boolean; | ||
@@ -72,6 +72,7 @@ private hasWrittenHeaders: boolean; | ||
this._rowTransform = null; | ||
this.headers = formatterOptions.headers; | ||
this.parsedHeaders = formatterOptions.hasProvidedHeaders && Array.isArray(formatterOptions.headers); | ||
this.hasWrittenHeaders = !formatterOptions.hasProvidedHeaders; | ||
if (this.parsedHeaders && this.headers !== null) { | ||
this.shouldWriteHeaders = formatterOptions.shouldWriteHeaders; | ||
this.hasWrittenHeaders = false; | ||
if (this.headers !== null) { | ||
this.fieldFormatter.headers = this.headers; | ||
@@ -102,4 +103,5 @@ } | ||
const { shouldFormatColumns, headers } = this.checkHeaders(transformedRow); | ||
if (headers) { | ||
if (this.shouldWriteHeaders && headers && !this.hasWrittenHeaders) { | ||
rows.push(this.formatColumns(headers, true)); | ||
this.hasWrittenHeaders = true; | ||
} | ||
@@ -118,26 +120,36 @@ if (shouldFormatColumns) { | ||
private checkHeaders(row: Row): { headers?: string[] | null; shouldFormatColumns: boolean } { | ||
if (!this.parsedHeaders) { | ||
this.parsedHeaders = true; | ||
this.headers = RowFormatter.gatherHeaders(row); | ||
this.fieldFormatter.headers = this.headers; | ||
if (this.headers) { | ||
// either the headers were provided by the user or we have already gathered them. | ||
return { shouldFormatColumns: true, headers: this.headers }; | ||
} | ||
if (this.hasWrittenHeaders) { | ||
const headers = RowFormatter.gatherHeaders(row); | ||
this.headers = headers; | ||
this.fieldFormatter.headers = headers; | ||
if (!this.shouldWriteHeaders) { | ||
// if we are not supposed to write the headers then | ||
// alwyas format the columns | ||
return { shouldFormatColumns: true, headers: null }; | ||
} | ||
this.hasWrittenHeaders = true; | ||
const shouldFormatColumns = RowFormatter.isHashArray(row) || !Array.isArray(row); | ||
return { shouldFormatColumns, headers: this.headers }; | ||
// if the row is equal to headers dont format | ||
return { shouldFormatColumns: !isEqual(headers, row), headers }; | ||
} | ||
private gatherColumns(row: Row): string[] { | ||
if (this.headers === null) { | ||
throw new Error('Headers is currently null'); | ||
} | ||
if (!Array.isArray(row)) { | ||
if (this.headers === null) { | ||
throw new Error('Headers is currently null'); | ||
} | ||
return this.headers.map((header): string => row[header]); | ||
} | ||
if (RowFormatter.isHashArray(row)) { | ||
return row.map((col): string => col[1]); | ||
return this.headers.map((header, i): string => { | ||
const col = row[i]; | ||
if (col) { | ||
return col[1]; | ||
} | ||
return ''; | ||
}); | ||
} | ||
return row; | ||
return this.headers.map((header, i): string => row[i]); | ||
} | ||
@@ -144,0 +156,0 @@ |
@@ -43,3 +43,3 @@ import { RowTransformFunction } from './types'; | ||
public readonly hasProvidedHeaders: boolean; | ||
public readonly shouldWriteHeaders: boolean; | ||
@@ -58,3 +58,3 @@ public readonly escapedQuote: string; | ||
} | ||
this.hasProvidedHeaders = !!this.headers; | ||
this.shouldWriteHeaders = !!this.headers; | ||
this.headers = Array.isArray(this.headers) ? this.headers : null; | ||
@@ -61,0 +61,0 @@ this.escapedQuote = `${this.escape}${this.quote}`; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2799
258042