* @property {string} [displayName=name] Optional `string` value to indicate the desired header name in the output CSV.
* @property {string} [defaultValue=''] Optional `string` value to use to fill empty CSV fields. If no value is provided, the
* field value will be `''` (empty string) in the output CSV.
+ * @property {Array<string>} [aliases=[]] Optional array of `string` values which are treated as alternate `name` values. A
+ * column name that matches an alias is treated the same as a column with the `name` name.
*/
/**
type: 'string',
displayName: name,
defaultValue: '',
- index: null
+ index: null,
+ aliases: []
});
}
else {
- const { name, type, displayName = name, defaultValue = '' } = header;
+ const { name, type, displayName = name, defaultValue = '', aliases = [] } = header;
let normalizedType = type?.toLowerCase() ?? 'string';
if (!['string', 'number', 'bigint', 'date'].includes(normalizedType)) {
console.warn(`Type "${normalizedType}" is not supported, defaulting to string.`);
type: normalizedType,
displayName,
defaultValue,
- index: null
+ index: null,
+ aliases: aliases.map(n => this.#useLiteralNames ? n : toCamelCase(n))
});
}
}
let i = 0;
for (const header of normalizedRow) {
const col = this.#useLiteralNames
- ? this.#columns.find(c => c.name === header)
- : this.#columns.find(c => c.name.toLocaleLowerCase() === header.toLocaleLowerCase());
+ ? this.#columns.find(c => [c.name, ...c.aliases].includes(header))
+ : this.#columns.find(c => [c.name, ...c.aliases].map(n => n.toLocaleLowerCase()).includes(header.toLocaleLowerCase()));
if (col !== undefined) { // Drop columns provided in the CSV but not the headers input
col.index = i;
}
['a4']
]));
});
+ test('removes columns that have the same name via aliases (last column prevails)', { concurrency: true }, async () => {
+ await createCSVMockStream([
+ ['columnA', 'column A', 'column-B', 'columnb', 'columnC'],
+ ['a1', 'a2', 'a3', 'a4', 'a5']
+ ])
+ .pipeThrough(new CSVNormalizer([
+ { name: 'columnA', aliases: ['columnB', 'columnC'] }
+ ]))
+ .pipeThrough(new CSVDenormalizer())
+ .pipeTo(csvStreamEqualWritable([
+ ['columnA'],
+ ['a5']
+ ]));
+ });
+ test('can match by column name alias', { concurrency: true }, async () => {
+ await createCSVMockStream([
+ ['aaa', 'bbb', 'ccc', 'ddd', 'eee'],
+ ['a1', 'a2', 'a3', 'a4', 'a5']
+ ])
+ .pipeThrough(new CSVNormalizer([
+ { name: 'zzz', aliases: ['yyy', 'ddd'] }
+ ]))
+ .pipeThrough(new CSVDenormalizer())
+ .pipeTo(csvStreamEqualWritable([
+ ['zzz'],
+ ['a4']
+ ]));
+ });
test('can match by literal column names', { concurrency: true }, async () => {
await createCSVMockStream([
['columnA', 'column A', 'column-A', 'columna'],
['a1', 'a2', 'a3', 'a4']
]));
});
+ test('can match by literal column name aliases', { concurrency: true }, async () => {
+ await createCSVMockStream([
+ ['columnA', 'column A', 'column-A', 'columna'],
+ ['a1', 'a2', 'a3', 'a4']
+ ])
+ .pipeThrough(new CSVNormalizer([
+ { name: 'aaa', aliases: ['columnA'] },
+ { name: 'bbb', aliases: ['column A'] },
+ { name: 'ccc', aliases: ['column-A'] },
+ { name: 'ddd', aliases: ['columna'] }
+ ], { useLiteralNames: true }))
+ .pipeThrough(new CSVDenormalizer())
+ .pipeTo(csvStreamEqualWritable([
+ ['aaa', 'bbb', 'ccc', 'ddd'],
+ ['a1', 'a2', 'a3', 'a4']
+ ]));
+ });
test('is NOT case sensitive on column names', { concurrency: true }, async () => {
await createCSVMockStream([
['datetime', 'dateTime'],