From 4b452c75c9865eeab2ab3f799db309dedb718149 Mon Sep 17 00:00:00 2001 From: Ardis Lu Date: Sat, 18 Oct 2025 21:30:41 -0700 Subject: [PATCH] Implement `.delete()` and `.clear()` Useful when a user wants to mostly duplicate an input row but make some minor tweaks. Not having the capability to remove rows leads to unwieldy workarounds. --- src/normalization.js | 32 ++++++++++++++++++++++++++++---- test/normalization.test.js | 18 ++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/normalization.js b/src/normalization.js index 32a57f9..7c1efee 100644 --- a/src/normalization.js +++ b/src/normalization.js @@ -346,15 +346,22 @@ export class CSVNormalizer extends TransformStream { * values by *reference* to the underlying `CSVNormalizerField`. This interception makes the `Map` stay in sync with the * `row` array. * - * Only the methods `.get()`, `.set()`, `.has()` and the property `.size` are supported. All other methods are unsupported. - * For more advanced usage, use the underlying `row` object directly. + * Only these methods and properties are supported: + * - `.get()` + * - `.set()` + * - `.has()` + * - `.delete()` + * - `.clear()` + * - `.size` + * + * All other methods are unsupported. For more advanced usage, use the underlying `row` object directly. * * The `displayName` and `emptyField` values of a field cannot be accessed from the `Map`. * * Calling `.set()` with a `name` that is not in the row will push a new field to the row. * @param {Array} row A normalized CSV row. - * @returns {Pick,'get'|'set'|'has'|'size'>} A `Proxy` over a `Map` object with keys set to the `name` of - * each field in the row. + * @returns {Pick,'get'|'set'|'has'|'delete'|'clear'|'size'>} A `Proxy` over a `Map` object with keys + * set to the `name` of each field in the row. */ static toFieldMap(row) { const map = new Map(); @@ -383,6 +390,23 @@ export class CSVNormalizer extends TransformStream { const targetProp = Reflect.get(target, prop); return typeof targetProp === 'function' ? targetProp.bind(target) : targetProp; } + if (prop === 'delete') { + return name => { + if (target.has(name)) { + const i = row.findIndex(f => f.name === name); + row.splice(i, 1); + target.delete(name); + return true; + } + return false; + } + } + if (prop === 'clear') { + return () => { + row.length = 0; + target.clear(); + } + } } }); } diff --git a/test/normalization.test.js b/test/normalization.test.js index d96e04b..97bbc0f 100644 --- a/test/normalization.test.js +++ b/test/normalization.test.js @@ -238,6 +238,24 @@ suite('CSVNormalizer.toFieldMap', { concurrency: true }, () => { map.set('b', 2).set('c', 3).set('d', 4); deepStrictEqual(row.length, 4); }); + test('can delete fields', { concurrency: true }, async () => { + const row = [{ name: 'a', value: 1 }, { name: 'b', value: 1 }]; + const map = CSVNormalizer.toFieldMap(row); + deepStrictEqual(row.length, 2); + ok(map.delete('a')); + ok(!map.delete('a')); + deepStrictEqual(row.length, 1); + ok(map.delete('b')); + ok(!map.delete('b')); + deepStrictEqual(row.length, 0); + }); + test('can clear fields', { concurrency: true }, async () => { + const row = [{ name: 'a', value: 1 }, { name: 'b', value: 1 }]; + const map = CSVNormalizer.toFieldMap(row); + deepStrictEqual(row.length, 2); + map.clear(); + deepStrictEqual(row.length, 0); + }); test('passes through .has() method', { concurrency: true }, async () => { const row = [{ name: 'a', value: 1 }]; const map = CSVNormalizer.toFieldMap(row); -- 2.47.3