From 7cff8cda8fc8ea1615fd182cfd7ae2c19356eaa6 Mon Sep 17 00:00:00 2001 From: Ardis Lu Date: Sat, 4 Oct 2025 22:51:22 -0700 Subject: [PATCH] Add `CSVNormalizer.toObject()` method --- examples/README.md | 2 ++ examples/data/ex4_3-in.csv | 4 ++++ examples/data/ex4_3-out.csv | 4 ++++ examples/ex4_3.js | 42 +++++++++++++++++++++++++++++++++++++ src/normalization.js | 16 ++++++++++++++ test/normalization.test.js | 28 +++++++++++++++++++++++++ 6 files changed, 96 insertions(+) create mode 100644 examples/data/ex4_3-in.csv create mode 100644 examples/data/ex4_3-out.csv create mode 100644 examples/ex4_3.js diff --git a/examples/README.md b/examples/README.md index 4238659..047d2ee 100644 --- a/examples/README.md +++ b/examples/README.md @@ -57,3 +57,5 @@ These examples demonstrate the extended API. [Example 4.1: Using `CSVNormalizer` and `CSVDenormalizer`](./ex4_1.js) ([input](./data/ex4_1-in.csv), [output](./data/ex4_1-out.csv)) [Example 4.2: Using `CSVNormalizer` and `CSVDenormalizer` with `CSVTransformer`](./ex4_2.js) ([input](./data/ex4_2-in.csv), [output](./data/ex4_2-out.csv)) + +[Example 4.3: Using `CSVNormalizer.toObject()`](./ex4_3.js) ([input](./data/ex4_3-in.csv), [output](./data/ex4_3-out.csv)) diff --git a/examples/data/ex4_3-in.csv b/examples/data/ex4_3-in.csv new file mode 100644 index 0000000..5f22363 --- /dev/null +++ b/examples/data/ex4_3-in.csv @@ -0,0 +1,4 @@ +a,b,c,🏴‍☠️,😂 +111,222,333,444,555 +111,222,333,444,555 +111,222,333,444,555 diff --git a/examples/data/ex4_3-out.csv b/examples/data/ex4_3-out.csv new file mode 100644 index 0000000..1e06ef7 --- /dev/null +++ b/examples/data/ex4_3-out.csv @@ -0,0 +1,4 @@ +a,b,c,🏴‍☠️,😂 +intercepted: 111,intercepted: 222,intercepted: 333,intercepted: 444,intercepted: 555 +intercepted: 111,intercepted: 222,intercepted: 333,intercepted: 444,intercepted: 555 +intercepted: 111,intercepted: 222,intercepted: 333,intercepted: 444,intercepted: 555 diff --git a/examples/ex4_3.js b/examples/ex4_3.js new file mode 100644 index 0000000..61f3975 --- /dev/null +++ b/examples/ex4_3.js @@ -0,0 +1,42 @@ +// ## Example 4.3: Using `CSVNormalizer.toObject()` +// +// Use the static method `CSVNormalizer.toObject()` if you just want to read the values of `CSVNormalizerField`. +// For more advanced usage, such as keeping the underlying `row` object in sync with changes to the value, see +// `CSVNormalizer.toFieldMap()`. + +import { CSVReader, CSVWriter, CSVNormalizer, CSVDenormalizer, CSVTransformer } from '../src/index.js'; +/** @import { CSVNormalizerHeader, CSVNormalizerField } from '../src/index.js'; */ + +/** @type {Array} */ +const headers = [ + { name: 'a' }, + { name: 'b' }, + { name: 'c' }, + { name: '🏴‍☠️' }, + { name: '😂' } +]; + +/** + * @param {Array} row + * @returns {Array} + */ +function transform(row) { + const obj = CSVNormalizer.toObject(row); + const { a, b, c } = obj; // Destructuring works + const pirate = obj['🏴‍☠️']; // Access by field name works + const laughing = obj['😂']; + const out = [ // Manually construct a new output row + { name: 'a', value: `intercepted: ${a}` }, + { name: 'b', value: `intercepted: ${b}` }, + { name: 'c', value: `intercepted: ${c}` }, + { name: '🏴‍☠️', value: `intercepted: ${pirate}` }, + { name: '😂', value: `intercepted: ${laughing}` }, + ]; + return out; +} + +await new CSVReader(new URL('./data/ex4_3-in.csv', import.meta.url)) + .pipeThrough(new CSVNormalizer(headers)) + .pipeThrough(new CSVTransformer(transform)) + .pipeThrough(new CSVDenormalizer()) + .pipeTo(new CSVWriter(new URL('./data/ex4_3-out.csv', import.meta.url))); diff --git a/src/normalization.js b/src/normalization.js index e9d7683..9312c04 100644 --- a/src/normalization.js +++ b/src/normalization.js @@ -309,6 +309,22 @@ export class CSVNormalizer extends TransformStream { console.warn(`Could not fix date: "${original}". Passing through as a string.`); return original; } + + /** + * Create a simple object that maps each `CSVNormalizerField` `name` to its `value`. Useful if you just want to read all the + * values in a normalized CSV row. + * + * The `displayName` and `emptyField` values of a field are lost in the resulting object. + * @param {Array} row A normalized CSV row. + * @returns {Record} An object where each key is a field `name` and the value is the field's `value`. + */ + static toObject(row) { + const object = {}; + for (const field of row) { + object[field.name] = field.value; + } + return object; + } } /** diff --git a/test/normalization.test.js b/test/normalization.test.js index df5b9d4..4c25b9c 100644 --- a/test/normalization.test.js +++ b/test/normalization.test.js @@ -181,6 +181,34 @@ suite('CSVNormalizer.fixExcelDate', { concurrency: true }, () => { } }); +suite('CSVNormalizer.toObject', { concurrency: true }, () => { + test('converts simple fields', { concurrency: true }, async () => { + const row = [ + { name: 'a', value: 1 }, + { name: 'b', value: 2 }, + { name: 'c', value: 3 } + ]; + const obj = CSVNormalizer.toObject(row); + deepStrictEqual(obj.a, 1); + deepStrictEqual(obj.b, 2); + deepStrictEqual(obj.c, 3); + }); + test('converts complex field names', { concurrency: true }, async () => { + const row = [ + { name: '🏴‍☠️', value: 1 } + ]; + const obj = CSVNormalizer.toObject(row); + deepStrictEqual(obj['🏴‍☠️'], 1); + }); + test('converts complex field values', { concurrency: true }, async () => { + const row = [ + { name: 'a', value: { 'b': [1n] } } + ]; + const obj = CSVNormalizer.toObject(row); + deepStrictEqual(obj.a.b[0], 1n); + }); +}); + suite('CSVNormalizer', { concurrency: true }, () => { test('can remove extra columns in CSV', { concurrency: true }, async () => { await createCSVMockStream([ -- 2.47.3