Skip to content

Commit 4f55981

Browse files
author
Will Munn
committed
Add support for multiple delimiters
1 parent 03ebbff commit 4f55981

File tree

7 files changed

+30
-11
lines changed

7 files changed

+30
-11
lines changed

documentation/docs/parsing/examples.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Parsed 1 rows
6464

6565
## Alternate Delimiter
6666

67-
You can provide a `delimiter` option to change the delimiter from a `,` character.
67+
You can provide a `delimiter` option to change the delimiter from a `,` character. If you specify an array, it will treat all of the characters as delimiters.
6868

6969
<Tabs
7070
defaultValue="ts"

documentation/docs/parsing/options.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ If set to `false` all data will be a JSON version of the row.
1414
## delimiter
1515
**Type**: `string` **Default**: `','`
1616

17-
The delimiter that will separate columns.
17+
**Type**: `string|string[]` **Default**: `','`
18+
19+
The delimiter(s) that will separate columns.
1820

1921
Set this option if your file uses an alternate delimiter such as `;` or `\t`. [Example](./examples.mdx#alternate-delimiter)
2022

2123
:::note
22-
When specifying an alternate delimiter you may only pass in a single character!
24+
When specifying an alternate delimiter you may only pass in a single character or list of single characters!
2325
:::
2426

2527

packages/parse/__tests__/ParserOptions.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('ParserOptions', () => {
2727

2828
it('should escape a custom delimiter', () => {
2929
expect(createOptions({ delimiter: '\\' }).delimiter).toBe('\\');
30-
expect(createOptions({ delimiter: '\\' }).escapedDelimiter).toBe('\\\\');
30+
expect(createOptions({ delimiter: '\\' }).escapedDelimiter).toEqual(['\\\\']);
3131
});
3232
});
3333

packages/parse/__tests__/parser/Parser.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ describe('Parser', () => {
1919
});
2020
});
2121

22+
it('should should support multiple potential field delimeters', () => {
23+
const data = 'first_name,last_name|email_address\tphone';
24+
const myParser = createParser({ delimiter: [',', '|', '\t'] });
25+
expect(parse(data, false, myParser)).toEqual({
26+
line: '',
27+
rows: [['first_name', 'last_name', 'email_address', 'phone']],
28+
});
29+
});
30+
2231
describe('with \\n', () => {
2332
describe('unescaped data', () => {
2433
it('should parse a block of CSV text', () => {

packages/parse/src/ParserOptions.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { HeaderArray, HeaderTransformFunction } from './types';
44

55
export interface ParserOptionsArgs {
66
objectMode?: boolean;
7-
delimiter?: string;
7+
delimiter?: string | string[];
88
quote?: string | null;
99
escape?: string;
1010
headers?: boolean | HeaderTransformFunction | HeaderArray;
@@ -23,7 +23,7 @@ export interface ParserOptionsArgs {
2323
}
2424

2525
export class ParserOptions {
26-
public readonly escapedDelimiter: string;
26+
public readonly escapedDelimiter: string[];
2727

2828
public readonly objectMode: boolean = true;
2929

@@ -71,13 +71,20 @@ export class ParserOptions {
7171

7272
public constructor(opts?: ParserOptionsArgs) {
7373
Object.assign(this, opts || {});
74-
if (this.delimiter.length > 1) {
74+
const delimiters = Array.isArray(this.delimiter) ? this.delimiter : [this.delimiter];
75+
if (
76+
delimiters.some((d: string) => {
77+
return d.length > 1;
78+
})
79+
) {
7580
throw new Error('delimiter option must be one character long');
7681
}
77-
this.escapedDelimiter = escapeRegExp(this.delimiter);
82+
this.escapedDelimiter = delimiters.map((d) => {
83+
return escapeRegExp(d);
84+
});
7885
this.escapeChar = this.escape ?? this.quote;
7986
this.supportsComments = !isNil(this.comment);
80-
this.NEXT_TOKEN_REGEXP = new RegExp(`([^\\s]|\\r\\n|\\n|\\r|${this.escapedDelimiter})`);
87+
this.NEXT_TOKEN_REGEXP = new RegExp(`([^\\s]|\\r\\n|\\n|\\r|${this.escapedDelimiter.join('|')})`);
8188

8289
if (this.maxRows > 0) {
8390
this.limitRows = true;

packages/parse/src/parser/Token.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ export class Token {
3131
}
3232

3333
public static isTokenDelimiter(token: Token, parserOptions: ParserOptions): boolean {
34-
return token.token === parserOptions.delimiter;
34+
const delimiter = Array.isArray(parserOptions.delimiter) ? parserOptions.delimiter : [parserOptions.delimiter];
35+
return delimiter.includes(token.token);
3536
}
3637

3738
public readonly token: string;

packages/parse/src/parser/column/QuotedColumnParser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export class QuotedColumnParser {
9898
// tldr: only part of the column was quoted
9999
const linePreview = scanner.lineFromCursor.substr(0, 10).replace(/[\r\n]/g, "\\n'");
100100
throw new Error(
101-
`Parse Error: expected: '${parserOptions.escapedDelimiter}' OR new line got: '${nextNonSpaceToken.token}'. at '${linePreview}'`,
101+
`Parse Error: expected: '${parserOptions.escapedDelimiter.join(',')}' OR new line got: '${nextNonSpaceToken.token}'. at '${linePreview}'`,
102102
);
103103
}
104104
scanner.advanceToToken(nextNonSpaceToken);

0 commit comments

Comments
 (0)