Skip to content

fix(csv): add columns options type for array items #6363

New issue

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

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

Already on GitHub? # to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
141 changes: 110 additions & 31 deletions csv/unstable_stringify.ts
Original file line number Diff line number Diff line change
@@ -85,11 +85,43 @@ export type ColumnDetails = {
*/
export type Column = ColumnDetails | PropertyAccessor | PropertyAccessor[];

/** An object (plain or array) */
export type DataItem = Readonly<Record<string, unknown> | unknown[]>;

/** Options for {@linkcode stringify}. */
export type StringifyOptions = {
/** Options for {@linkcode stringify} with an array of of arrays as data. */
export type ArrayStringifyOptions = {
/** Whether to include the row of headers or not.
*
* @default {true}
*/
headers?: boolean;
/**
* Delimiter used to separate values. Examples:
* - `","` _comma_
* - `"\t"` _tab_
* - `"|"` _pipe_
* - etc.
*
* @default {","}
*/
separator?: string;
/**
* A list of instructions for how to target and transform the data for each
* column of output. This is also where you can provide an explicit header
* name for the column.
*
* @default {undefined}
*/
columns?: readonly number[] | undefined;
/**
* Whether to add a
* {@link https://en.wikipedia.org/wiki/Byte_order_mark | byte-order mark} to the
* beginning of the file content. Required by software such as MS Excel to
* properly display Unicode text.
*
* @default {false}
*/
bom?: boolean;
};
/** Options for {@linkcode stringify} with an array of of objects as data. */
export type ObjectStringifyOptions = {
/** Whether to include the row of headers or not.
*
* @default {true}
@@ -123,21 +155,23 @@ export type StringifyOptions = {
*/
bom?: boolean;
};
/** Options for {@linkcode stringify}. */
export type StringifyOptions = ArrayStringifyOptions | ObjectStringifyOptions;

/**
* Converts an array of objects into a CSV string.
*
* @example Default options
* @example Give an array of objects without specifying columns
* ```ts
* import { stringify } from "@std/csv/unstable-stringify";
* import { assertEquals } from "@std/assert/equals";
*
* const data = [
* ["Rick", 70],
* ["Morty", 14],
* { name: "Rick", age: 70 },
* { name: "Morty", age: 14 },
* ];
*
* assertEquals(stringify(data), `Rick,70\r\nMorty,14\r\n`);
* assertEquals(stringify(data), `name,age\r\nRick,70\r\nMorty,14\r\n`);
* ```
*
* @example Give an array of objects and specify columns
@@ -155,19 +189,6 @@ export type StringifyOptions = {
* assertEquals(stringify(data, { columns }), `name,age\r\nRick,70\r\nMorty,14\r\n`);
* ```
*
* @example Give an array of objects without specifying columns
* ```ts
* import { stringify } from "@std/csv/unstable-stringify";
* import { assertEquals } from "@std/assert/equals";
*
* const data = [
* { name: "Rick", age: 70 },
* { name: "Morty", age: 14 },
* ];
*
* assertEquals(stringify(data), `name,age\r\nRick,70\r\nMorty,14\r\n`);
* ```
*
* @example Give an array of objects and specify columns with `headers: false`
* ```ts
* import { stringify } from "@std/csv/unstable-stringify";
@@ -178,10 +199,8 @@ export type StringifyOptions = {
* { name: "Morty", age: 14 },
* ];
*
* const columns = ["name", "age"];
*
* assertEquals(
* stringify(data, { columns, headers: false }),
* stringify(data, { headers: false }),
* `Rick,70\r\nMorty,14\r\n`,
* );
* ```
@@ -280,9 +299,21 @@ export type StringifyOptions = {
* );
* ```
*
* @param data The source data to stringify. It's an array of items which are
* plain objects or arrays.
* @param options Options for the stringification.
* @returns A CSV string.
*/
export function stringify(
data: Record<string, unknown>[],
options?: ObjectStringifyOptions,
): string;
/**
* Converts an array of arrays into a CSV string.
*
* @example Give an array of string arrays and specify columns with renaming
* ```ts
* import { stringify } from "@std/csv/unstable-stringify";
* import { stringify } from "@std/csv/stringify";
* import { assertEquals } from "@std/assert/equals";
*
* const data = [
@@ -303,7 +334,7 @@ export type StringifyOptions = {
*
* @example Emit TSV (tab-separated values) with `separator: "\t"`
* ```ts
* import { stringify } from "@std/csv/unstable-stringify";
* import { stringify } from "@std/csv/stringify";
* import { assertEquals } from "@std/assert/equals";
*
* const data = [
@@ -316,7 +347,7 @@ export type StringifyOptions = {
*
* @example Prepend a byte-order mark with `bom: true`
* ```ts
* import { stringify } from "@std/csv/unstable-stringify";
* import { stringify } from "@std/csv/stringify";
* import { assertEquals } from "@std/assert/equals";
*
* const data = [["Rick", 70]];
@@ -330,8 +361,54 @@ export type StringifyOptions = {
* @returns A CSV string.
*/
export function stringify(
data: readonly DataItem[],
options?: StringifyOptions,
data: unknown[][],
options?: ArrayStringifyOptions,
): string;
/**
* Converts an array of objects or arrays into a CSV string.
*
* @example Give an array of objects without specifying columns
* ```ts
* import { stringify } from "@std/csv/unstable-stringify";
* import { assertEquals } from "@std/assert/equals";
*
* const data = [
* { name: "Rick", age: 70 },
* { name: "Morty", age: 14 },
* ];
*
* assertEquals(stringify(data), `name,age\r\nRick,70\r\nMorty,14\r\n`);
* ```
*
* @example Give an array of string arrays and specify columns with renaming
* ```ts
* import { stringify } from "@std/csv/stringify";
* import { assertEquals } from "@std/assert/equals";
*
* const data = [
* ["Rick", 70],
* ["Morty", 14],
* ];
*
* const columns = [
* { prop: 0, header: "name" },
* { prop: 1, header: "age" },
* ];
*
* assertEquals(
* stringify(data, { columns }),
* `name,age\r\nRick,70\r\nMorty,14\r\n`,
* );
* ```
*
* @param data The source data to stringify. It's an array of items which are
* plain objects or arrays.
* @param options Options for the stringification.
* @returns A CSV string.
*/
export function stringify(
data: unknown[][] | Record<string, unknown>[],
options?: StringifyOptions | ArrayStringifyOptions,
): string {
let { columns } = options ?? {};

@@ -349,7 +426,9 @@ export function stringify(
/**
* Infers the columns from the first object element of the given array.
*/
function inferColumns(data: readonly DataItem[]): string[] {
function inferColumns(
data: unknown[][] | Record<string, unknown>[],
): string[] {
const firstElement = data.at(0);
if (
firstElement &&