diff --git a/README.md b/README.md
index 50eeda2..6187017 100644
--- a/README.md
+++ b/README.md
@@ -43,43 +43,51 @@ var data = d3.csvParse(string);
## API Reference
-# d3.csvParse(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/csv.js#L5 "Source")
+# d3.csvParse(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/csv.js "Source")
Equivalent to [dsvFormat](#dsvFormat)(",").[parse](#dsv_parse).
-# d3.csvParseRows(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/csv.js#L6 "Source")
+# d3.csvParseRows(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/csv.js "Source")
Equivalent to [dsvFormat](#dsvFormat)(",").[parseRows](#dsv_parseRows).
-# d3.csvFormat(rows[, columns]) [<>](https://github.com/d3/d3-dsv/blob/master/src/csv.js#L7 "Source")
+# d3.csvFormat(rows[, columns]) [<>](https://github.com/d3/d3-dsv/blob/master/src/csv.js "Source")
Equivalent to [dsvFormat](#dsvFormat)(",").[format](#dsv_format).
-# d3.csvFormatRows(rows) [<>](https://github.com/d3/d3-dsv/blob/master/src/csv.js#L8 "Source")
+# d3.csvFormatBody(rows[, columns]) [<>](https://github.com/d3/d3-dsv/blob/master/src/csv.js "Source")
+
+Equivalent to [dsvFormat](#dsvFormat)(",").[formatBody](#dsv_formatBody).
+
+# d3.csvFormatRows(rows) [<>](https://github.com/d3/d3-dsv/blob/master/src/csv.js "Source")
Equivalent to [dsvFormat](#dsvFormat)(",").[formatRows](#dsv_formatRows).
-# d3.tsvParse(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/tsv.js#L5 "Source")
+# d3.tsvParse(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/tsv.js "Source")
Equivalent to [dsvFormat](#dsvFormat)("\t").[parse](#dsv_parse).
-# d3.tsvParseRows(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/tsv.js#L6 "Source")
+# d3.tsvParseRows(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/tsv.js "Source")
Equivalent to [dsvFormat](#dsvFormat)("\t").[parseRows](#dsv_parseRows).
-# d3.tsvFormat(rows[, columns]) [<>](https://github.com/d3/d3-dsv/blob/master/src/tsv.js#L7 "Source")
+# d3.tsvFormat(rows[, columns]) [<>](https://github.com/d3/d3-dsv/blob/master/src/tsv.js "Source")
Equivalent to [dsvFormat](#dsvFormat)("\t").[format](#dsv_format).
-# d3.tsvFormatRows(rows) [<>](https://github.com/d3/d3-dsv/blob/master/src/tsv.js#L8 "Source")
+# d3.tsvFormatBody(rows[, columns]) [<>](https://github.com/d3/d3-dsv/blob/master/src/tsv.js "Source")
+
+Equivalent to [dsvFormat](#dsvFormat)("\t").[formatBody](#dsv_formatBody).
+
+# d3.tsvFormatRows(rows) [<>](https://github.com/d3/d3-dsv/blob/master/src/tsv.js "Source")
Equivalent to [dsvFormat](#dsvFormat)("\t").[formatRows](#dsv_formatRows).
-# d3.dsvFormat(delimiter) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js#L30)
+# d3.dsvFormat(delimiter) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js)
Constructs a new DSV parser and formatter for the specified *delimiter*. The *delimiter* must be a single character (*i.e.*, a single 16-bit code unit); so, ASCII delimiters are fine, but emoji delimiters are not.
-# *dsv*.parse(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js#L34 "Source")
+# *dsv*.parse(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js "Source")
Parses the specified *string*, which must be in the delimiter-separated values format with the appropriate delimiter, returning an array of objects representing the parsed rows.
@@ -123,7 +131,7 @@ var data = d3.csvParse(string, function(d) {
Note: using `+` rather than [parseInt](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt) or [parseFloat](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseFloat) is typically faster, though more restrictive. For example, `"30px"` when coerced using `+` returns `NaN`, while parseInt and parseFloat return `30`.
-# dsv.parseRows(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js#L43 "Source")
+# dsv.parseRows(string[, row]) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js "Source")
Parses the specified *string*, which must be in the delimiter-separated values format with the appropriate delimiter, returning an array of arrays representing the parsed rows.
@@ -160,7 +168,7 @@ var data = d3.csvParseRows(string, function(d, i) {
In effect, *row* is similar to applying a [map](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map) and [filter](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter) operator to the returned rows.
-# dsv.format(rows[, columns]) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js#L105 "Source")
+# dsv.format(rows[, columns]) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js "Source")
Formats the specified array of object *rows* as delimiter-separated values, returning a string. This operation is the inverse of [*dsv*.parse](#dsv_parse). Each row will be separated by a newline (`\n`), and each column within each row will be separated by the delimiter (such as a comma, `,`). Values that contain either the delimiter, a double-quote (`"`) or a newline will be escaped using double-quotes.
@@ -172,7 +180,11 @@ var string = d3.csvFormat(data, ["year", "make", "model", "length"]);
All fields on each row object will be coerced to strings. If the field value is null or undefined, the empty string is used. If the field value is a Date, the ISO 8601 date format is used per [*date*.toISOString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString). For more control over which and how fields are formatted, first map *rows* to an array of array of string, and then use [*dsv*.formatRows](#dsv_formatRows).
-# dsv.formatRows(rows) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js#L114 "Source")
+# dsv.formatBody(rows[, columns]) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js "Source")
+
+Equivalent to [*dsv*.format](#dsv_format), but omits the header row. This is useful, for example, when appending rows to an existing file.
+
+# dsv.formatRows(rows) [<>](https://github.com/d3/d3-dsv/blob/master/src/dsv.js "Source")
Formats the specified array of array of string *rows* as delimiter-separated values, returning a string. This operation is the reverse of [*dsv*.parseRows](#dsv_parseRows). Each row will be separated by a newline (`\n`), and each column within each row will be separated by the delimiter (such as a comma, `,`). Values that contain either the delimiter, a double-quote (") or a newline will be escaped using double-quotes.
diff --git a/src/csv.js b/src/csv.js
index b85be9c..aa9144a 100644
--- a/src/csv.js
+++ b/src/csv.js
@@ -5,4 +5,5 @@ var csv = dsv(",");
export var csvParse = csv.parse;
export var csvParseRows = csv.parseRows;
export var csvFormat = csv.format;
+export var csvFormatBody = csv.formatBody;
export var csvFormatRows = csv.formatRows;
diff --git a/src/dsv.js b/src/dsv.js
index 99f0d14..8aba35c 100644
--- a/src/dsv.js
+++ b/src/dsv.js
@@ -95,13 +95,22 @@ export default function(delimiter) {
return rows;
}
- function format(rows, columns) {
- if (columns == null) columns = inferColumns(rows);
- return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) {
+ function preformatBody(rows, columns) {
+ return rows.map(function(row) {
return columns.map(function(column) {
return formatValue(row[column]);
}).join(delimiter);
- })).join("\n");
+ });
+ }
+
+ function format(rows, columns) {
+ if (columns == null) columns = inferColumns(rows);
+ return [columns.map(formatValue).join(delimiter)].concat(preformatBody(rows, columns)).join("\n");
+ }
+
+ function formatBody(rows, columns) {
+ if (columns == null) columns = inferColumns(rows);
+ return preformatBody(rows, columns).join("\n");
}
function formatRows(rows) {
@@ -123,6 +132,7 @@ export default function(delimiter) {
parse: parse,
parseRows: parseRows,
format: format,
+ formatBody: formatBody,
formatRows: formatRows
};
}
diff --git a/src/index.js b/src/index.js
index 0473f32..b3053b9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,4 @@
export {default as dsvFormat} from "./dsv";
-export {csvParse, csvParseRows, csvFormat, csvFormatRows} from "./csv";
-export {tsvParse, tsvParseRows, tsvFormat, tsvFormatRows} from "./tsv";
+export {csvParse, csvParseRows, csvFormat, csvFormatBody, csvFormatRows} from "./csv";
+export {tsvParse, tsvParseRows, tsvFormat, tsvFormatBody, tsvFormatRows} from "./tsv";
export {default as autoType} from "./autoType";
diff --git a/src/tsv.js b/src/tsv.js
index 5e8b363..9fe418a 100644
--- a/src/tsv.js
+++ b/src/tsv.js
@@ -5,4 +5,5 @@ var tsv = dsv("\t");
export var tsvParse = tsv.parse;
export var tsvParseRows = tsv.parseRows;
export var tsvFormat = tsv.format;
+export var tsvFormatBody = tsv.formatBody;
export var tsvFormatRows = tsv.formatRows;
diff --git a/test/csv-test.js b/test/csv-test.js
index d97180f..9d70ecb 100644
--- a/test/csv-test.js
+++ b/test/csv-test.js
@@ -299,6 +299,16 @@ tape("csvFormat(array, columns) coerces field values to strings", function(test)
test.end();
});
+tape("csvFormatBody(array) omits the header row", function(test) {
+ test.deepEqual(dsv.csvFormatBody([{a: 1, b: 2}, {c: 3, b: 4}, {c: 5, a: 1, b: 2}]), "1,2,\n,4,3\n1,2,5");
+ test.end();
+});
+
+tape("csvFormatBody(array, columns) omits the header row", function(test) {
+ test.deepEqual(dsv.csvFormatBody([{a: 1, b: 2}, {c: 3, b: 4}, {c: 5, a: 1, b: 2}], ["a", "b"]), "1,2\n,4\n1,2");
+ test.end();
+});
+
tape("csvFormatRows(array) takes an array of array of string as input", function(test) {
test.deepEqual(dsv.csvFormatRows([["a", "b", "c"], ["1", "2", "3"]]), "a,b,c\n1,2,3");
test.end();