forked from GrosSacASac/utilsac
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdeep.js
More file actions
231 lines (207 loc) · 6.2 KB
/
deep.js
File metadata and controls
231 lines (207 loc) · 6.2 KB
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
export {
deepCopy,
deepCopyAdded,
deepAssign,
deepAssignAdded,
deepEqual,
};
/**
only works with undefined, null, Number, Symbol, String, Big Int, Object, Array,
warning does not work with cyclic object, Date, regex
does not work with anything created with new
*/
const deepCopy = x => {
if (typeof x !== `object` || x === null) {
return x;
}
if (Array.isArray(x)) {
return x.map(deepCopy);
}
const copy = {}
Object.entries(x).forEach(([key, value]) => {
copy[key] = deepCopy(value);
});
return copy;
};
/**
todo
like deepCopy but supports more types
only works with
undefined, null, Number, Symbol, String, Big Int, Object, Array, Date, RegExp, Set, Map
warning does not work with cyclic object
does not work with anything created with new
*/
const deepCopyAdded = x => {
if (typeof x !== `object` || x === null) {
return x;
}
if (x instanceof Date) {
return new Date(x);
}
if (x instanceof RegExp) {
return new RegExp(x);
}
if (x instanceof Set) {
return new Set(Array.from(x, deepCopyAdded));
}
if (x instanceof Map) {
const map = new Map();
// todo keep internal links
x.forEach((value, key) => {
map.set(deepCopyAdded(key), deepCopyAdded(value));
});
return map;
}
if (Array.isArray(x)) {
return x.map(deepCopy);
}
const copy = {}
Object.entries(x).forEach(([key, value]) => {
copy[key] = deepCopy(value);
});
return copy;
};
/**
Like Object.assign but deep,
does not try to assign partial arrays inside, they are overwritten
only works with undefined, null, Number, Symbol, String, Big Int, Object, Array,
warning does not work with cyclic object, Date, regex
does not work with anything created with new
@param {Object} target must be an object
@param {Object} source1 should be an object, silently discards if not (like Object.assign)
@return {Object} target
*/
const deepAssign = (target, ...sources) => {
sources.forEach(source => {
if (!source || typeof source !== `object`) {
return;
}
Object.entries(source).forEach(([key, value]) => {
if (key === `__proto__`) {
return;
}
if (typeof value !== `object` || value === null) {
target[key] = value;
return;
}
if (Array.isArray(value)) {
target[key] = [];
}
// value is an Object
if (typeof target[key] !== `object` || !target[key]) {
target[key] = {};
}
deepAssign(target[key], value);
});
});
return target;
};
/**
Like deepAssign but supports more types,
does not try to assign partial arrays inside, they are overwritten
only works with
undefined, null, Number, Symbol, String, Big Int, Object, Array, Date, Set, Map, RegExp
warning does not work with cyclic objects
@param {Object} target must be an object
@param {Object} source1 should be an object, silently discards if not (like Object.assign)
@return {Object} target
*/
const deepAssignAdded = (target, ...sources) => {
sources.forEach(source => {
if (!source || typeof source !== `object`) {
return;
}
Object.entries(source).forEach(([key, value]) => {
if (key === `__proto__`) {
return;
}
if (typeof value !== `object` || value === null) {
target[key] = value;
return;
}
if (value instanceof Date) {
target[key] = new Date(value);
return;
}
if (value instanceof RegExp) {
target[key] = new RegExp(value);
return;
}
if (value instanceof Set) {
let tempArray = Array.from(value, deepCopyAdded);
if (target[key] instanceof Set) {
tempArray = tempArray.concat(Array.from(target[key]));
}
target[key] = new Set(Array.from(value, deepCopyAdded));
return;
}
if (value instanceof Map) {
let map;
if (target[key] instanceof Map) {
map = target[key];
} else {
map = new Map();
target[key] = map;
}
// todo keep internal links
value.forEach((value, key) => {
map.set(deepCopyAdded(key), deepCopyAdded(value));
});
return;
}
if (Array.isArray(value)) {
target[key] = [];
}
// value is an Object
if (typeof target[key] !== `object` || !target[key]) {
target[key] = {};
}
deepAssign(target[key], value);
});
});
return target;
};
/**
* Determines whether two objects are equal. Works on nested structures.
* @param {Object} obj1 can be either an object or array
* @param {Object} obj2 can be either an object or array
* @returns {Boolean}
*/
const deepEqual = (obj1, obj2) => {
if (obj1 === obj2) { // check primative
return true;
}
if (Array.isArray(obj1)) {
if (!Array.isArray(obj2)) {
return false;
}
if (obj1.length !== obj2.length) {
return false;
}
for (let i = 0; i < obj1.length; i += 1) {
if (!deepEqual(obj1[i], obj2[i])) {
return false;
}
}
return true;
}
if (isObject(obj1) && obj1 !== null
|| (isObject(obj2) && obj2 !== null)) {
const keysA = Object.keys(obj1);
const keysB = Object.keys(obj2);
if (keysA.length !== keysB.length) {
return false;
}
for (const prop in obj1) {
if (Object.prototype.hasOwnProperty.call(obj2, prop)) {
if (!deepEqual(obj1[prop], obj2[prop])) {
return false;
}
}
}
return true;
}
};
const isObject = obj => {
return obj === Object(obj);
};