cleanObjectDeep
Recursively removes null, undefined, and empty string values from an object or array.
1/**
2 * Recursively removes null, undefined, and empty string values from an object or array.
3 *
4 * Options:
5 * - predicate: A function to determine if a value should be removed (defaults to null/undefined/'').
6 * - preserveEmptyObjects: If true, keeps empty objects after cleanup.
7 * - preserveEmptyArrays: If true, keeps empty arrays after cleanup.
8 * - fallback: A value to return instead of omitting the value.
9 *
10 * @param value - The input structure (object, array, or primitive).
11 * @param options - Optional configuration object.
12 * @returns A deeply cleaned version of the input or fallback if removed.
13 */
14export function cleanObjectDeep(
15 value: any,
16 options: {
17 predicate?: (value: any) => boolean;
18 preserveEmptyObjects?: boolean;
19 preserveEmptyArrays?: boolean;
20 fallback?: any;
21 } = {}
22): any {
23 const {
24 predicate = defaultPredicate,
25 preserveEmptyObjects = false,
26 preserveEmptyArrays = false,
27 fallback,
28 } = options;
29
30 if (Array.isArray(value)) {
31 const cleaned = value
32 .map(item => cleanObjectDeep(item, options))
33 .filter(item => !predicate(item));
34
35 if (cleaned.length === 0 && !preserveEmptyArrays) {
36 return fallback !== undefined ? fallback : undefined;
37 }
38
39 return cleaned;
40 }
41
42 if (value && typeof value === 'object' && value.constructor === Object) {
43 const result: Record<string, any> = {};
44 for (const [key, val] of Object.entries(value)) {
45 const cleaned = cleanObjectDeep(val, options);
46 if (!predicate(cleaned)) {
47 result[key] = cleaned;
48 }
49 }
50
51 if (Object.keys(result).length === 0 && !preserveEmptyObjects) {
52 return fallback !== undefined ? fallback : undefined;
53 }
54
55 return result;
56 }
57
58 return predicate(value) ? fallback !== undefined ? fallback : undefined : value;
59}
60
61function defaultPredicate(val: any): boolean {
62 return val === null || val === undefined || val === '';
63}
Recursive Deep Cleaning
Traverses deeply nested structures (objects and arrays) to remove or replace unwanted values at any depth.
Highly Configurable Behavior
Supports options like
predicate
,fallback
,preserveEmptyObjects
, andpreserveEmptyArrays
, offering fine-grained control.Array and Object Support
Treats both arrays and plain objects appropriately, preserving type-specific behavior during cleanup.
Custom Predicate Logic
Allows users to define what "empty" means, enabling use cases beyond null/undefined/empty string.
Safe and Non-Destructive
Returns new structures rather than mutating the original, ensuring immutability and functional safety.
Tests | Examples
1test('cleanObjectDeep - fallback replaces removed values', () => {
2 const input = {
3 a: '',
4 b: null,
5 c: undefined,
6 d: { e: '', f: { g: null } },
7 };
8
9 expect(cleanObjectDeep(input, { fallback: 'REMOVED' })).toEqual({
10 a: 'REMOVED',
11 b: 'REMOVED',
12 c: 'REMOVED',
13 d: {
14 e: 'REMOVED',
15 f: { g: 'REMOVED' },
16 },
17 });
18});
19
20test('cleanObjectDeep - fallback returned for fully removed object', () => {
21 const input = { a: null, b: undefined };
22 expect(cleanObjectDeep(input, { fallback: 'FALLBACK' })).toEqual('FALLBACK');
23});
24
25test('cleanObjectDeep - fallback in nested arrays', () => {
26 const input = [null, '', { a: undefined }];
27 expect(cleanObjectDeep(input, { fallback: 0 })).toEqual([0, 0, { a: 0 }]);
28});
29
30test('cleanObjectDeep - fallback + preserveEmptyArrays/Objects', () => {
31 const input = {
32 arr: [null],
33 obj: { a: null },
34 };
35
36 expect(cleanObjectDeep(input, {
37 fallback: '-',
38 preserveEmptyArrays: true,
39 preserveEmptyObjects: true,
40 })).toEqual({
41 arr: [],
42 obj: {},
43 });
44});
Common Use Cases
Form Submission Sanitization
Remove unused, empty, or irrelevant fields from deeply nested form data before sending to the server.
API Request Preparation
Clean dynamic payloads before POST/PUT requests to reduce payload size and avoid validation errors.
Content Filtering and Normalization
Clean user-generated or external data before storage or processing in a CMS, CRM, or database.
Config Validation and Compression
Strip out empty overrides or default-equivalent values in deeply nested config trees.
Privacy and Redaction Tools
Remove sensitive or empty data fields from logs, exports, or user-facing payloads.