Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add header option to toCSV method. #370

Merged
merged 3 commits into from
Sep 24, 2024
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
2 changes: 1 addition & 1 deletion docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ This method performs parsing only. To both load and parse a CSV file, use [loadC
* *options*: A CSV format options object:
* *delimiter*: A single-character delimiter string between column values (default `','`).
* *decimal*: A single-character numeric decimal separator (default `'.'`).
* *header*: Boolean flag (default `true`) to specify the presence of a header row. If `true`, indicates the CSV contains a header row with column names. If `false`, indicates the CSV does not contain a header row and the columns are given the names `'col1'`, `'col2'`, etc unless the *names* option is specified.
* *header*: Boolean flag (default `true`) to specify the presence of a header row. If `true`, indicates the CSV contains a header row with column names. If `false`, indicates the CSV does not contain a header row and the columns are given the names `'col1'`, `'col2'`, etc unless the *names* option is specified.
* *names*: An array of column names to use for header-less CSV files. This option is ignored if the *header* option is `true`.
* *skip*: The number of lines to skip (default `0`) before reading data.
* *comment*: A string used to identify comment lines. Any lines that start with the comment pattern are skipped.
Expand Down
1 change: 1 addition & 0 deletions docs/api/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ Format this table as a comma-separated values (CSV) string. Other delimiters, su

* *options*: A formatting options object:
* *delimiter*: The delimiter between values (default `","`).
* *header*: Boolean flag (default `true`) to specify the presence of a header row. If `true`, includes a header row with column names. If `false`, the header is omitted.
* *limit*: The maximum number of rows to print (default `Infinity`).
* *offset*: The row offset indicating how many initial rows to skip (default `0`).
* *columns*: Ordered list of column names to include. If function-valued, the function should accept a table as input and return an array of column name strings. Otherwise, should be an array of name strings.
Expand Down
14 changes: 9 additions & 5 deletions src/format/to-csv.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import isDate from '../util/is-date.js';
* Options for CSV formatting.
* @typedef {object} CSVFormatOptions
* @property {string} [delimiter=','] The delimiter between values.
* @property {boolean} [header=true] Flag to specify presence of header row.
* If true, includes a header row with column names.
* If false, the header is omitted.
* @property {number} [limit=Infinity] The maximum number of rows to print.
* @property {number} [offset=0] The row offset indicating how many initial rows to skip.
* @property {import('./util.js').ColumnSelectOptions} [columns] Ordered list
Expand All @@ -29,6 +32,7 @@ export default function(table, options = {}) {
const names = columns(table, options.columns);
const format = options.format || {};
const delim = options.delimiter || ',';
const header = options.header ?? true;
const reFormat = new RegExp(`["${delim}\n\r]`);

const formatValue = value => value == null ? ''
Expand All @@ -37,16 +41,16 @@ export default function(table, options = {}) {
: value;

const vals = names.map(formatValue);
let text = '';
let text = header ? (vals.join(delim) + '\n') : '';

scan(table, names, options.limit || Infinity, options.offset, {
row() {
text += vals.join(delim) + '\n';
},
cell(value, name, index) {
vals[index] = formatValue(format[name] ? format[name](value) : value);
},
end() {
text += vals.join(delim) + '\n';
}
});

return text + vals.join(delim);
return text;
}
10 changes: 7 additions & 3 deletions src/format/to-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,22 @@ export default function(table, options = {}) {
+ tag('tbody');

scan(table, names, options.limit, options.offset, {
row(row) {
start(row) {
r = row;
text += (++idx ? '</tr>' : '') + tag('tr');
++idx;
text += tag('tr');
},
cell(value, name) {
text += tag('td', name, 1)
+ formatter(value, format[name])
+ '</td>';
},
end() {
text += '</tr>';
}
});

return text + '</tr></tbody></table>';
return text + '</tbody></table>';
}

function styles(options) {
Expand Down
11 changes: 7 additions & 4 deletions src/format/to-markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,19 @@ export default function(table, options = {}) {
+ names.map(escape).join('|')
+ '|\n|'
+ names.map(name => alignValue(align[name])).join('|')
+ '|';
+ '|\n';

scan(table, names, options.limit, options.offset, {
row() {
text += '\n|';
start() {
text += '|';
},
cell(value, name) {
text += escape(formatValue(value, format[name])) + '|';
},
end() {
text += '\n';
}
});

return text + '\n';
return text;
}
7 changes: 5 additions & 2 deletions src/format/util.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import inferFormat from './infer.js';
import isFunction from '../util/is-function.js';
import identity from '../util/identity.js';

/**
* Column selection function.
Expand Down Expand Up @@ -59,13 +60,15 @@ function values(table, columnName) {
}

export function scan(table, names, limit = 100, offset, ctx) {
const { start = identity, cell, end = identity } = ctx;
const data = table.data();
const n = names.length;
table.scan(row => {
ctx.row(row);
start(row);
for (let i = 0; i < n; ++i) {
const name = names[i];
ctx.cell(data[names[i]].at(row), name, i);
cell(data[name].at(row), name, i);
}
end(row);
}, true, limit, offset);
}
28 changes: 22 additions & 6 deletions test/format/to-csv-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ const tabText = text.map(t => t.split(',').join('\t'));
describe('toCSV', () => {
it('formats delimited text', () => {
const dt = new ColumnTable(data());
assert.equal(toCSV(dt), text.join('\n'), 'csv text');
assert.equal(toCSV(dt), text.join('\n') + '\n', 'csv text');
assert.equal(
toCSV(dt, { limit: 2, columns: ['str', 'int'] }),
text.slice(0, 3)
.map(s => s.split(',').slice(0, 2).join(','))
.join('\n'),
.join('\n') + '\n',
'csv text with limit'
);
});
Expand All @@ -37,24 +37,40 @@ describe('toCSV', () => {
const dt = new ColumnTable(data());
assert.equal(
toCSV(dt, { delimiter: '\t' }),
tabText.join('\n'),
tabText.join('\n') + '\n',
'csv text with delimiter'
);
assert.equal(
toCSV(dt, { limit: 2, delimiter: '\t', columns: ['str', 'int'] }),
text.slice(0, 3)
.map(s => s.split(',').slice(0, 2).join('\t'))
.join('\n'),
.join('\n') + '\n',
'csv text with delimiter and limit'
);
});

it('formats delimited text with header option', () => {
const dt = new ColumnTable(data());
assert.equal(
toCSV(dt, { header: false }),
text.slice(1).join('\n') + '\n',
'csv text without header'
);
assert.equal(
toCSV(dt, { header: false, limit: 2, columns: ['str', 'int'] }),
text.slice(1, 3)
.map(s => s.split(',').slice(0, 2).join(','))
.join('\n') + '\n',
'csv text without header and with limit'
);
});

it('formats delimited text for filtered table', () => {
const bs = new BitSet(3).not(); bs.clear(1);
const dt = new ColumnTable(data(), null, bs);
assert.equal(
toCSV(dt),
[ ...text.slice(0, 2), ...text.slice(3) ].join('\n'),
[ ...text.slice(0, 2), ...text.slice(3) ].join('\n') + '\n',
'csv text with limit'
);
});
Expand All @@ -63,7 +79,7 @@ describe('toCSV', () => {
const dt = new ColumnTable(data());
assert.equal(
toCSV(dt, { limit: 2, columns: ['str'], format: { str: d => d + '!' } }),
['str', 'a!', 'b!'].join('\n'),
['str', 'a!', 'b!'].join('\n') + '\n',
'csv text with custom format'
);
});
Expand Down