Skip to content

Commit 1560564

Browse files
committed
fix(formatter,#503): Do not ignore rows when headers is false
* Fixes issue where row contents were ignored if the first row is empty and headers is false
1 parent e3165c7 commit 1560564

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

documentation/docs/formatting/options.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ Set to `true` if you want the first character written to the stream to be a utf-
5858
**Type**: `null|boolean|string[]` **Default**: `null`
5959

6060
If `true` then the headers will be auto detected from the first row. [Example](./examples.mdx#auto-discovery)
61-
* If the row is a one-dimensional array then headers is a no-op
62-
* If the row is an object then the keys will be used.
63-
* If the row is an array of two element arrays (`[ ['header', 'column'], ['header2', 'column2'] ]`) then the first element in each array will be used.
61+
* If the row is a one-dimensional array then the `headers` will be the first row processed
62+
* If the row is an object then the keys will be used.
63+
* If the row is an array of two element arrays (`[ ['header', 'column'], ['header2', 'column2'] ]`) then the first element in each array will be used.
6464

6565
If there is not a headers row and you want to provide one then set to a `string[]`. [Example](./examples.mdx#provided-headers)
6666

packages/format/__tests__/formatter/RowFormatter.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ describe('RowFormatter', () => {
9393
const formatter = createFormatter({ headers: false });
9494
await expect(formatRow(headerRow, formatter)).resolves.toEqual([headerRow.join(',')]);
9595
});
96+
97+
it('should still format all rows without headers', async () => {
98+
const formatter = createFormatter({ headers: false });
99+
await expect(formatRow([], formatter)).resolves.toEqual(['']);
100+
await expect(formatRow(headerRow, formatter)).resolves.toEqual([`\n${headerRow.join(',')}`]);
101+
});
96102
});
97103

98104
describe('with headers=true', () => {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { RecordingStream } from '../__fixtures__';
2+
import { RowArray, write } from '../../src';
3+
4+
describe('Issue #503 - https://github.com/C2FO/fast-csv/issues/503', () => {
5+
it('should emit all columns after an empty row', () => {
6+
return new Promise((res, rej) => {
7+
const rs = new RecordingStream();
8+
const data: RowArray[] = [[], ['something']];
9+
10+
write(data, { quote: false, headers: false, writeHeaders: false })
11+
.pipe(rs)
12+
.on('error', rej)
13+
.on('finish', () => {
14+
expect(rs.data).toEqual(['\nsomething']);
15+
res();
16+
});
17+
});
18+
});
19+
20+
it('should not assume first row is a header if header = false', () => {
21+
return new Promise((res, rej) => {
22+
const rs = new RecordingStream();
23+
const data: RowArray[] = [['1'], [], ['1', '2', '3']];
24+
25+
write(data, { quote: false, headers: false, writeHeaders: false })
26+
.pipe(rs)
27+
.on('error', rej)
28+
.on('finish', () => {
29+
expect(rs.data).toEqual(['1', '\n', '\n1,2,3']);
30+
res();
31+
});
32+
});
33+
});
34+
});

packages/format/src/formatter/RowFormatter.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@ type RowFormatterTransform<I extends Row, O extends Row> = (row: I, cb: RowTrans
99
type RowFormatterCallback = (error: Error | null, data?: RowArray) => void;
1010

1111
export class RowFormatter<I extends Row, O extends Row> {
12-
private static isHashArray(row: Row): row is RowHashArray {
12+
private static isRowHashArray(row: Row): row is RowHashArray {
1313
if (Array.isArray(row)) {
1414
return Array.isArray(row[0]) && row[0].length === 2;
1515
}
1616
return false;
1717
}
1818

19+
private static isRowArray(row: Row): row is RowArray {
20+
return Array.isArray(row) && !this.isRowHashArray(row);
21+
}
22+
1923
// get headers from a row item
2024
private static gatherHeaders(row: Row): string[] {
21-
if (RowFormatter.isHashArray(row)) {
25+
if (RowFormatter.isRowHashArray(row)) {
2226
// lets assume a multi-dimesional array with item 0 being the header
2327
return row.map((it): string => it[0]);
2428
}
@@ -130,7 +134,6 @@ export class RowFormatter<I extends Row, O extends Row> {
130134
// either the headers were provided by the user or we have already gathered them.
131135
return { shouldFormatColumns: true, headers: this.headers };
132136
}
133-
134137
const headers = RowFormatter.gatherHeaders(row);
135138
this.headers = headers;
136139
this.fieldFormatter.headers = headers;
@@ -151,7 +154,7 @@ export class RowFormatter<I extends Row, O extends Row> {
151154
if (!Array.isArray(row)) {
152155
return this.headers.map((header): string => row[header] as string);
153156
}
154-
if (RowFormatter.isHashArray(row)) {
157+
if (RowFormatter.isRowHashArray(row)) {
155158
return this.headers.map((header, i): string => {
156159
const col = (row[i] as unknown) as string;
157160
if (col) {
@@ -160,6 +163,11 @@ export class RowFormatter<I extends Row, O extends Row> {
160163
return '';
161164
});
162165
}
166+
// if its a one dimensional array and headers were not provided
167+
// then just return the row
168+
if (RowFormatter.isRowArray(row) && !this.shouldWriteHeaders) {
169+
return row;
170+
}
163171
return this.headers.map((header, i): string => row[i]);
164172
}
165173

0 commit comments

Comments
 (0)