diff --git a/README.md b/README.md index 75c3029..9914e4c 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,26 @@ 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], nested: { key3: 'a', key4: [] } })); +// { nested: { key3: 'a' } } + +console.log( + removeUndefinedObjects( + { key1: [], key2: [undefined], nested: { key3: 'a', key4: [] } }, + { preserveEmptyArray: true }, + ), +); +// { key1: [], key2: [], nested: { key3: 'a', key4: [] } } +``` + ### `removeAllFalsy` Optional boolean. @@ -42,6 +62,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/src/index.ts b/src/index.ts index c375afa..986f93f 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; } @@ -42,6 +47,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; } @@ -57,6 +64,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; } @@ -84,8 +93,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 1a8b28f..ace2336 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -34,6 +34,36 @@ 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( + { 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: [] } } } }, + { 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', () => { const obj = { a: {