Skip to content

Commit 6bc9175

Browse files
authored
feat: add preserve empty array option (#148)
* feat: add preserve empty array option * feat: update readme & more test coverage * style: merge from next & lint * doc: add more example in readme
1 parent ebe37f1 commit 6bc9175

File tree

3 files changed

+65
-5
lines changed

3 files changed

+65
-5
lines changed

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ console.log(removeUndefinedObjects({ key: [], key2: 123 }));
2121

2222
## Behavior
2323

24-
Any items with the following value will be removed:
24+
Any items with the following value will be removed by default:
2525

2626
- Empty object, `{}`
2727
- Empty array, `[]`
@@ -34,6 +34,26 @@ The following items will NOT be removed:
3434

3535
## Options
3636

37+
### `preserveEmptyArray`
38+
39+
Optional boolean.
40+
If provided, empty arrays `[]` will not get removed
41+
42+
```js
43+
import removeUndefinedObjects from 'remove-undefined-objects';
44+
45+
console.log(removeUndefinedObjects({ key1: [], key2: [undefined], nested: { key3: 'a', key4: [] } }));
46+
// { nested: { key3: 'a' } }
47+
48+
console.log(
49+
removeUndefinedObjects(
50+
{ key1: [], key2: [undefined], nested: { key3: 'a', key4: [] } },
51+
{ preserveEmptyArray: true },
52+
),
53+
);
54+
// { key1: [], key2: [], nested: { key3: 'a', key4: [] } }
55+
```
56+
3757
### `removeAllFalsy`
3858

3959
Optional boolean.
@@ -42,6 +62,6 @@ If provided, the empty string `''` and `null` will be removed as well.
4262
```js
4363
import removeUndefinedObjects from 'remove-undefined-objects';
4464

45-
console.log(removeUndefinedObjects({ key1: null, key2: 123, key3: '' }), { removeAllFalsy: true });
65+
console.log(removeUndefinedObjects({ key1: null, key2: 123, key3: '' }, { removeAllFalsy: true }));
4666
// { key2: 123 }
4767
```

src/index.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ function isObject(obj: unknown) {
33
}
44

55
function isEmptyObject(obj: unknown) {
6-
return typeof obj === 'object' && obj !== null && !Object.keys(obj).length;
6+
return typeof obj === 'object' && obj !== null && !Array.isArray(obj) && !Object.keys(obj).length;
7+
}
8+
9+
function isEmptyArray(arr: unknown) {
10+
return Array.isArray(arr) && arr.length === 0;
711
}
812

913
interface RemovalOptions {
14+
preserveEmptyArray?: boolean;
1015
removeAllFalsy?: boolean;
1116
}
1217

@@ -42,6 +47,8 @@ function stripEmptyObjects(obj: any, options: RemovalOptions = {}) {
4247

4348
if (isEmptyObject(value)) {
4449
delete cleanObj[key];
50+
} else if (isEmptyArray(value) && !options.preserveEmptyArray) {
51+
delete cleanObj[key];
4552
} else {
4653
cleanObj[key] = value;
4754
}
@@ -57,6 +64,8 @@ function stripEmptyObjects(obj: any, options: RemovalOptions = {}) {
5764

5865
if (isEmptyObject(value)) {
5966
delete cleanObj[idx];
67+
} else if (isEmptyArray(value) && !options.preserveEmptyArray) {
68+
delete cleanObj[idx];
6069
} else {
6170
cleanObj[idx] = value;
6271
}
@@ -84,8 +93,9 @@ export default function removeUndefinedObjects<T>(obj?: T, options?: RemovalOpti
8493
// Then we recursively remove all empty objects and nullish arrays.
8594
withoutUndefined = stripEmptyObjects(withoutUndefined, options);
8695

87-
// If the only thing that's leftover is an empty object then return nothing.
88-
if (isEmptyObject(withoutUndefined)) return undefined;
96+
// If the only thing that's leftover is an empty object or empty array then return nothing.
97+
if (isEmptyObject(withoutUndefined) || (isEmptyArray(withoutUndefined) && !options?.preserveEmptyArray))
98+
return undefined;
8999

90100
return withoutUndefined;
91101
}

test/index.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,36 @@ test("should also remove '' and null values when removeAllFalsy is true", () =>
3434
expect(removeUndefinedObjects({ value: undefined }, { removeAllFalsy: true })).toBeUndefined();
3535
});
3636

37+
test('should not remove empty arrays when preserveEmptyArray is true', () => {
38+
expect(removeUndefinedObjects({ value: [] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] });
39+
expect(removeUndefinedObjects({ value: [undefined] }, { preserveEmptyArray: true })).toStrictEqual({ value: [] });
40+
expect(
41+
removeUndefinedObjects(
42+
{ key1: [], key2: [undefined], nested: { key3: 'a', key4: [] } },
43+
{ preserveEmptyArray: true },
44+
),
45+
).toStrictEqual({ key1: [], key2: [], nested: { key3: 'a', key4: [] } });
46+
expect(
47+
removeUndefinedObjects(
48+
{ value: { a: 'a', nested: { b: 'b', nested2: { c: [undefined], d: [] } } } },
49+
{ preserveEmptyArray: true },
50+
),
51+
).toStrictEqual({ value: { a: 'a', nested: { b: 'b', nested2: { c: [], d: [] } } } });
52+
});
53+
54+
test('should leave alone non-falsey values when preserveEmptyArray is true', () => {
55+
expect(removeUndefinedObjects({ value: { a: [1, 2, 3] }, b: null }, { preserveEmptyArray: true })).toStrictEqual({
56+
value: { a: [1, 2, 3] },
57+
b: null,
58+
});
59+
expect(
60+
removeUndefinedObjects(
61+
{ value: { a: [1, undefined], nested: { b: 'b', c: [undefined], d: [] } } },
62+
{ preserveEmptyArray: true },
63+
),
64+
).toStrictEqual({ value: { a: [1], nested: { b: 'b', c: [], d: [] } } });
65+
});
66+
3767
test('should remove empty objects with only empty properties', () => {
3868
const obj = {
3969
a: {

0 commit comments

Comments
 (0)