From f2fc78520279cec78a3413d4e5781eb628002ce6 Mon Sep 17 00:00:00 2001 From: eagletrhost Date: Wed, 10 Sep 2025 20:36:16 +1000 Subject: [PATCH 1/4] feat: add preserve empty array option --- src/index.ts | 16 +++++++++++++--- test/index.test.ts | 6 ++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 75e01e0..264bced 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,10 +3,15 @@ function isObject(obj: unknown) { } function isEmptyObject(obj: unknown) { - return typeof obj === 'object' && obj !== null && !Object.keys(obj).length; + return typeof obj === 'object' && obj !== null && !Array.isArray(obj) && !Object.keys(obj).length; +} + +function isEmptyArray(arr: unknown) { + return Array.isArray(arr) && arr.length === 0; } interface RemovalOptions { + preserveEmptyArray?: boolean; removeAllFalsy?: boolean; } @@ -41,6 +46,8 @@ function stripEmptyObjects(obj: any, options: RemovalOptions = {}) { if (isEmptyObject(value)) { delete cleanObj[key]; + } else if (isEmptyArray(value) && !options.preserveEmptyArray) { + delete cleanObj[key]; } else { cleanObj[key] = value; } @@ -56,6 +63,8 @@ function stripEmptyObjects(obj: any, options: RemovalOptions = {}) { if (isEmptyObject(value)) { delete cleanObj[idx]; + } else if (isEmptyArray(value) && !options.preserveEmptyArray) { + delete cleanObj[idx]; } else { cleanObj[idx] = value; } @@ -83,8 +92,9 @@ export default function removeUndefinedObjects(obj?: T, options?: RemovalOpti // Then we recursively remove all empty objects and nullish arrays. withoutUndefined = stripEmptyObjects(withoutUndefined, options); - // If the only thing that's leftover is an empty object then return nothing. - if (isEmptyObject(withoutUndefined)) return undefined; + // If the only thing that's leftover is an empty object or empty array then return nothing. + if (isEmptyObject(withoutUndefined) || (isEmptyArray(withoutUndefined) && !options?.preserveEmptyArray)) + return undefined; return withoutUndefined; } diff --git a/test/index.test.ts b/test/index.test.ts index 8e529d5..2ef74ec 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -34,6 +34,12 @@ test("should also remove '' and null values when removeAllFalsy is true", () => expect(removeUndefinedObjects({ value: undefined }, { removeAllFalsy: true })).toBeUndefined(); }); +test('should not remove empty arrays when preserveEmptyArray is true', () => { + expect(removeUndefinedObjects({ value: [] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] }); + expect(removeUndefinedObjects({ value: [undefined] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] }); + expect(removeUndefinedObjects({ value: [null] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] }); +}); + test('should remove empty objects with only empty properties', () => { const obj = { a: { From 2b2d8c10cdfe6e66cfa5eda345140229ea782d42 Mon Sep 17 00:00:00 2001 From: eagletrhost Date: Thu, 11 Sep 2025 08:35:09 +1000 Subject: [PATCH 2/4] feat: update readme & more test coverage --- README.md | 16 ++++++++++++++-- test/index.test.ts | 20 +++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0729099..131cf1d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ console.log(removeUndefinedObjects({key: [], key2: 123})); ## Behavior -Any items with the following value will be removed: +Any items with the following value will be removed by default: * Empty object, `{}` * Empty array, `[]` @@ -34,6 +34,18 @@ The following items will NOT be removed: ## Options +### `preserveEmptyArray` + +Optional boolean. +If provided, empty arrays `[]` will not get removed + +```js +import removeUndefinedObjects from 'remove-undefined-objects'; + +console.log(removeUndefinedObjects({key1: [], key2: [undefined], key3: {key4: 'a', key5: []}}, {preserveEmptyArray: true})); +// { key1: [], key2: [], key3: { key4: 'a', key5: [] } } +``` + ### `removeAllFalsy` Optional boolean. @@ -42,6 +54,6 @@ If provided, the empty string `''` and `null` will be removed as well. ```js import removeUndefinedObjects from 'remove-undefined-objects'; -console.log(removeUndefinedObjects({key1: null, key2: 123, key3: ''}), {removeAllFalsy: true}); +console.log(removeUndefinedObjects({key1: null, key2: 123, key3: ''}, {removeAllFalsy: true})); // { key2: 123 } ``` diff --git a/test/index.test.ts b/test/index.test.ts index 2ef74ec..874fa68 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -37,7 +37,25 @@ test("should also remove '' and null values when removeAllFalsy is true", () => test('should not remove empty arrays when preserveEmptyArray is true', () => { expect(removeUndefinedObjects({ value: [] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] }); expect(removeUndefinedObjects({ value: [undefined] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] }); - expect(removeUndefinedObjects({ value: [null] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] }); + expect( + removeUndefinedObjects( + { value: { a: 'a', nested: { b: 'b', nested2: { c: [undefined], d: [] } } } }, + { preserveEmptyArray: true }, + ), + ).toStrictEqual({ value: { a: 'a', nested: { b: 'b', nested2: { c: [], d: [] } } } }); +}); + +test('should leave alone non-falsey values when preserveEmptyArray is true', () => { + expect(removeUndefinedObjects({ value: { a: [1, 2, 3] }, b: null }, { preserveEmptyArray: true })).toStrictEqual({ + value: { a: [1, 2, 3] }, + b: null, + }); + expect( + removeUndefinedObjects( + { value: { a: [1, undefined], nested: { b: 'b', c: [undefined], d: [] } } }, + { preserveEmptyArray: true }, + ), + ).toStrictEqual({ value: { a: [1], nested: { b: 'b', c: [], d: [] } } }); }); test('should remove empty objects with only empty properties', () => { From 9556ad13f49224c3d64bd516b8f7e474c184a502 Mon Sep 17 00:00:00 2001 From: eagletrhost Date: Thu, 11 Sep 2025 09:07:08 +1000 Subject: [PATCH 3/4] style: merge from next & lint --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9b1cff6..a7c686c 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,9 @@ If provided, empty arrays `[]` will not get removed ```js import removeUndefinedObjects from 'remove-undefined-objects'; -console.log(removeUndefinedObjects({key1: [], key2: [undefined], key3: {key4: 'a', key5: []}}, {preserveEmptyArray: true})); +console.log( + removeUndefinedObjects({ key1: [], key2: [undefined], key3: { key4: 'a', key5: [] } }, { preserveEmptyArray: true }), +); // { key1: [], key2: [], key3: { key4: 'a', key5: [] } } ``` @@ -54,6 +56,6 @@ If provided, the empty string `''` and `null` will be removed as well. ```js import removeUndefinedObjects from 'remove-undefined-objects'; -console.log(removeUndefinedObjects({key1: null, key2: 123, key3: ''}, {removeAllFalsy: true})); +console.log(removeUndefinedObjects({ key1: null, key2: 123, key3: '' }, { removeAllFalsy: true })); // { key2: 123 } ``` From 0be4a2e8e9a13a6f59934632eefcbe7a4ccdb301 Mon Sep 17 00:00:00 2001 From: eagletrhost Date: Thu, 11 Sep 2025 10:40:11 +1000 Subject: [PATCH 4/4] doc: add more example in readme --- README.md | 10 ++++++++-- test/index.test.ts | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a7c686c..9914e4c 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,16 @@ If provided, empty arrays `[]` will not get removed ```js import removeUndefinedObjects from 'remove-undefined-objects'; +console.log(removeUndefinedObjects({ key1: [], key2: [undefined], nested: { key3: 'a', key4: [] } })); +// { nested: { key3: 'a' } } + console.log( - removeUndefinedObjects({ key1: [], key2: [undefined], key3: { key4: 'a', key5: [] } }, { preserveEmptyArray: true }), + removeUndefinedObjects( + { key1: [], key2: [undefined], nested: { key3: 'a', key4: [] } }, + { preserveEmptyArray: true }, + ), ); -// { key1: [], key2: [], key3: { key4: 'a', key5: [] } } +// { key1: [], key2: [], nested: { key3: 'a', key4: [] } } ``` ### `removeAllFalsy` diff --git a/test/index.test.ts b/test/index.test.ts index 90e6ffe..ace2336 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -37,6 +37,12 @@ test("should also remove '' and null values when removeAllFalsy is true", () => test('should not remove empty arrays when preserveEmptyArray is true', () => { expect(removeUndefinedObjects({ value: [] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] }); expect(removeUndefinedObjects({ value: [undefined] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] }); + expect( + removeUndefinedObjects( + { key1: [], key2: [undefined], nested: { key3: 'a', key4: [] } }, + { preserveEmptyArray: true }, + ), + ).toStrictEqual({ key1: [], key2: [], nested: { key3: 'a', key4: [] } }); expect( removeUndefinedObjects( { value: { a: 'a', nested: { b: 'b', nested2: { c: [undefined], d: [] } } } },