Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 06b79f6

Browse files
committedFeb 15, 2023
added clean object fn
1 parent 5dfb705 commit 06b79f6

File tree

6 files changed

+213
-54
lines changed

6 files changed

+213
-54
lines changed
 

‎src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export { createURLParams } from './lib/create-url-params';
22
export { createFormData } from './lib/create-form-data';
3+
export { cleanObject } from './lib/clean-object';
34
export * from './types/create-url-params';
45
export * from './types/create-form-data';
6+
export * from './types/clean-object';

‎src/lib/clean-object.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { CleanObjectConfig, Record } from '../types/clean-object';
2+
3+
const defaultConfig: CleanObjectConfig = {
4+
removeEmptyValues: false,
5+
};
6+
7+
const needSkipValue = (
8+
value: unknown,
9+
{ removeEmptyValues }: CleanObjectConfig
10+
): boolean => {
11+
if (value === null || value === undefined) {
12+
return true;
13+
}
14+
if (typeof value !== 'string') {
15+
return false;
16+
}
17+
return value === '' && removeEmptyValues;
18+
};
19+
20+
export const cleanObject = (
21+
obj: Record,
22+
config?: Partial<CleanObjectConfig>
23+
): Record => {
24+
const cfg = Object.assign({ ...defaultConfig }, config || {});
25+
26+
if (obj === null || obj === undefined) {
27+
return {};
28+
}
29+
30+
if (obj instanceof Blob || obj instanceof File || obj instanceof Date) {
31+
return obj;
32+
}
33+
34+
if (Array.isArray(obj)) {
35+
return obj.reduce((acc, curr) => {
36+
return [...acc, cleanObject(curr)];
37+
}, []);
38+
}
39+
40+
const keys = Object.keys(obj);
41+
if (!keys.length) {
42+
return {};
43+
}
44+
45+
return keys.reduce((acc: Record, key) => {
46+
const item = obj[key];
47+
if (needSkipValue(item, cfg)) {
48+
return acc;
49+
}
50+
if (Array.isArray(item)) {
51+
if (!item.length && cfg.removeEmptyValues) {
52+
return acc;
53+
}
54+
const mapped = item.reduce((arr: Record[], curr) => {
55+
if (typeof curr === 'object' && curr) {
56+
const cleared = cleanObject(curr, cfg);
57+
if (!Object.keys(cleared).length && cfg.removeEmptyValues) {
58+
return arr;
59+
}
60+
return [...arr, cleared];
61+
}
62+
const skip = needSkipValue(curr, cfg);
63+
return skip ? arr : [...arr, curr];
64+
}, []);
65+
return { ...acc, [`${key}`]: mapped };
66+
}
67+
68+
if (typeof item === 'object' && item) {
69+
const mapped = cleanObject(item as Record, cfg);
70+
if (!Object.keys(mapped).length && cfg.removeEmptyValues) {
71+
return acc;
72+
}
73+
return { ...acc, [`${key}`]: mapped };
74+
}
75+
76+
return { ...acc, [`${key}`]: item };
77+
}, {});
78+
};

‎src/lib/create-form-data.ts

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,17 @@ const fillFormData = <T extends object>(
4444
}
4545
} else if (typeof value === 'object') {
4646
if (value instanceof File) {
47-
formData.append(
48-
keyPrefix,
49-
value,
50-
value.name
51-
);
52-
}
53-
else if(value instanceof Blob){
54-
formData.append(
55-
keyPrefix,
56-
value,
57-
);
47+
formData.append(keyPrefix, value, value.name);
48+
} else if (value instanceof Blob) {
49+
formData.append(keyPrefix, value);
5850
} else {
5951
Object.keys(value).forEach((key) => {
6052
const keyString = getKeyString(keyPrefix, key, index);
61-
fillFormData(value[key as keyof object], { ...config, keyPrefix: keyString }, formData);
53+
fillFormData(
54+
value[key as keyof object],
55+
{ ...config, keyPrefix: keyString },
56+
formData
57+
);
6258
});
6359
}
6460
} else {
@@ -76,7 +72,7 @@ const defaultConfig = {
7672
booleanMapper: (val: boolean) => (val ? '1' : '0'),
7773
allowNullableValues: false,
7874
allowEmptyValues: false,
79-
}
75+
};
8076
/**
8177
* fill form data recursive function
8278
* @param value - form value
@@ -89,7 +85,7 @@ export const createFormData = <T extends object>(
8985
existingFormData?: FormData
9086
): FormData => {
9187
// create config from default and argument options
92-
const config = Object.assign({...defaultConfig},options || {});
88+
const config = Object.assign({ ...defaultConfig }, options || {});
9389

9490
// return form data if value instanceof FormData
9591
if (value instanceof FormData) {
@@ -102,6 +98,6 @@ export const createFormData = <T extends object>(
10298
}
10399
// fill form data by form value and return
104100
const formData = existingFormData || new FormData();
105-
fillFormData({...value}, config, formData);
101+
fillFormData({ ...value }, config, formData);
106102
return formData;
107103
};

‎src/lib/create-url-params.ts

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
import {
2-
CreateURLParamsConfig
3-
} from '../types/create-url-params';
1+
import { CreateURLParamsConfig } from '../types/create-url-params';
42

53
import { getKeyString } from './utils/get-key-string';
64

75
const defaultConfig: CreateURLParamsConfig = {
86
keyPrefix: '',
97
toString: false,
108
index: null,
11-
booleanMapper: (val: boolean) => (val ? '1' : '0')
9+
booleanMapper: (val: boolean) => (val ? '1' : '0'),
1210
};
1311

1412
export const createURLParams = <T extends object>(
@@ -24,14 +22,14 @@ export const createURLParams = <T extends object>(
2422

2523
if (Array.isArray(value)) {
2624
return value.reduce((acc: object, curr, i) => {
27-
if (typeof curr === 'object') {
25+
if (typeof curr === 'object' && curr) {
2826
return {
2927
...acc,
3028
...createURLParams(curr, {
3129
...config,
3230
keyPrefix,
33-
index: i
34-
})
31+
index: i,
32+
}),
3533
};
3634
}
3735
if (!keyPrefix || curr === null || curr === undefined) {
@@ -41,44 +39,41 @@ export const createURLParams = <T extends object>(
4139
typeof curr === 'boolean'
4240
? booleanMapper(curr)
4341
: toString
44-
? String(curr)
45-
: curr;
42+
? String(curr)
43+
: curr;
4644
return { ...acc, [`${keyPrefix}[${i}]`]: value };
4745
}, {});
4846
}
4947

50-
return Object.keys(value).reduce(
51-
(acc: object, key: string) => {
52-
const keyString = getKeyString(keyPrefix, key, index);
53-
const item = value[key as keyof object];
48+
return Object.keys(value).reduce((acc: object, key: string) => {
49+
const keyString = getKeyString(keyPrefix, key, index);
50+
const item = value[key as keyof object];
5451

55-
if (Array.isArray(item)) {
56-
return {
57-
...acc,
58-
...createURLParams(item, {
59-
...config,
60-
keyPrefix: keyString
61-
})
62-
};
63-
}
52+
if (Array.isArray(item)) {
53+
return {
54+
...acc,
55+
...createURLParams(item, {
56+
...config,
57+
keyPrefix: keyString,
58+
}),
59+
};
60+
}
6461

65-
if (typeof item === 'boolean') {
66-
return { ...acc, [`${keyString}`]: booleanMapper(item) };
67-
}
62+
if (typeof item === 'boolean') {
63+
return { ...acc, [`${keyString}`]: booleanMapper(item) };
64+
}
6865

69-
if (item && typeof item === 'object') {
70-
return {
71-
...acc,
72-
...createURLParams(item, { ...config, keyPrefix: keyString })
73-
};
74-
}
66+
if (item && typeof item === 'object') {
67+
return {
68+
...acc,
69+
...createURLParams(item, { ...config, keyPrefix: keyString }),
70+
};
71+
}
7572

76-
if (item !== null && item !== undefined) {
77-
const val = toString ? String(item) : item;
78-
return { ...acc, [`${keyString}`]: val };
79-
}
80-
return acc;
81-
},
82-
{}
83-
);
73+
if (item !== null && item !== undefined) {
74+
const val = toString ? String(item) : item;
75+
return { ...acc, [`${keyString}`]: val };
76+
}
77+
return acc;
78+
}, {});
8479
};

‎src/types/clean-object.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export interface Record {
2+
[key: string]: unknown;
3+
}
4+
5+
export interface CleanObjectConfig {
6+
removeEmptyValues: boolean;
7+
}

‎test/clean-object.spec.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { expect, use } from 'chai';
2+
import { describe, it } from 'mocha';
3+
import deepEqualInAnyOrder from 'deep-equal-in-any-order';
4+
5+
import { cleanObject } from '../src';
6+
7+
use(deepEqualInAnyOrder);
8+
9+
describe('cleanObject function', () => {
10+
it('should be success for simple values', () => {
11+
expect(cleanObject({})).to.deep.equal({});
12+
expect(cleanObject(null)).to.deep.equal({});
13+
const date = new Date();
14+
expect(cleanObject(date)).to.deep.equal(date);
15+
expect(cleanObject([])).to.deep.equal([]);
16+
});
17+
18+
it('should be success for nullable values obj', () => {
19+
const file = new File([], 'test');
20+
const obj = {
21+
id: 1,
22+
name: 'test',
23+
surName: '',
24+
email: undefined,
25+
address: null,
26+
subscription: {
27+
active: false,
28+
status: null,
29+
file,
30+
previous: [],
31+
obj: {},
32+
},
33+
file,
34+
simpleArray: ['', 'test', null],
35+
arr: [{ id: 1, name: '', active: null }, null, { id: 3 }, {}, [], file]
36+
};
37+
const expectedResult = {
38+
id: 1,
39+
name: 'test',
40+
surName: '',
41+
subscription: {
42+
active: false,
43+
file,
44+
previous: [],
45+
obj: {},
46+
},
47+
file,
48+
simpleArray: ['', 'test'],
49+
arr: [{ id: 1, name: '' }, { id: 3 }, {}, [], file]
50+
};
51+
expect(cleanObject(obj)).to.deep.equal(expectedResult);
52+
});
53+
it('should be success for empty string and array in obj', () => {
54+
const obj = {
55+
id: 1,
56+
name: 'test',
57+
surName: '',
58+
address: null,
59+
subscription: {
60+
active: false,
61+
status: '',
62+
previous: [{ id: 1, status: null }],
63+
emptyArr: [],
64+
obj: {},
65+
},
66+
simpleArray: ['', 'test', null],
67+
arr: [{ id: 1, name: '' }, null, {}, []]
68+
};
69+
const expectedResult = {
70+
id: 1,
71+
name: 'test',
72+
subscription: {
73+
active: false,
74+
previous: [{ id: 1 }]
75+
},
76+
simpleArray: ['test'],
77+
arr: [{ id: 1 }]
78+
};
79+
expect(cleanObject(obj, { removeEmptyValues: true })).to.deep.equal(expectedResult);
80+
});
81+
});

0 commit comments

Comments
 (0)
Please sign in to comment.