forked from darksinge/deep-cleaner
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
104 lines (87 loc) · 3.06 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/**
* deepCleaner.js :: Delete nested key-value pairs by a specified key
* or remove empty objects, empty strings, null, and undefined
* values from an object.
*/
'use strict';
const utils = require('./utils');
/**
* cleanCyclicObject :: Removes any undefined, null, or empty strings, arrays, or objects from `obj`.
* Uses a `WeakMap` to keep track of objects that have been visited while recursively cleaning
* an object to prevent infinite recursive calls.
* @param {Object} object :: the object to be cleaned
* @param {?String} target :: Optional key to remove from `object`. If not specified, the default
* behavior is to remove "empty" values from `object`. A value is considered to be empty if it
* is one of the following:
* - empty strings
* - empty arrays
* - empty objects
* - values that are null
* - values that are undefined
*/
function cleanCyclicObject(object, target=null) {
const visitedObjects = new WeakMap(); // use a WeakMap to keep track of which objects have been visited
function recursiveClean(obj) {
// If `obj` is an actual object, check if it's been seen already.
if (utils.isObject(obj)) {
// If we've seen this object already, return to stop infinite loops
if (visitedObjects.has(obj)) {
return;
}
// If we haven't seen this object yet, add it to the list of visited objects.
// Since 'obj' itself is used as the key, the value of 'objects[obj]' is
// irrelevent. I just went with using 'null'.
visitedObjects.set(obj, null);
for (var key in obj) {
if (
(target && key === target) // Check if 'key' is the target to delete,
|| (!target && utils.isEmpty(obj[key])) // or if 'target' is unspecified but the object is "empty"
) {
delete obj[key];
} else {
recursiveClean(obj[key]);
if (utils.isEmpty(obj[key]))
delete obj[key];
}
}
// If 'obj' is an array, check it's elements for objects to clean up.
} else if (utils.isArray(obj)) {
for (var i in obj) {
recursiveClean(obj[i]);
}
var cleanup=[]
for (var i in obj) {
if (utils.isEmpty(obj[i]))
cleanup.push(i)
}
for (var i in cleanup)
delete obj[cleanup[i]]
}
}
recursiveClean(object);
}
/**
* removeKeyLoop :: does the same thing as `removeKey()` but with multiple keys.
* @param {Object} obj :: the object being cleaned
* @param {String|Array} keys :: an array containing keys to be cleaned from `obj`
*/
function removeKeyLoop(obj, keys) {
for (var key of keys) {
cleanCyclicObject(obj, key);
}
}
/**
* deepCleaner
*
* @param {Object} obj :: the object being cleaned
* @param {?String|?Array} target :: A string or array of strings of key(s) for key-value pair(s) to be cleaned from `obj`
*/
function deepCleaner(obj, target = null) {
if (utils.isArray(target)) {
removeKeyLoop(obj, target);
} else {
cleanCyclicObject(obj, target);
}
return obj;
}
module.exports = deepCleaner;