@hichchi/utils 0.0.1-beta.2 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.default.js +1 -0
- package/index.cjs.js +3645 -0
- package/index.cjs.mjs +2 -0
- package/index.d.ts +1 -5
- package/index.esm.js +3575 -0
- package/package.json +13 -8
- package/src/index.d.ts +5 -0
- package/{interfaces → src/interfaces}/infinite-object.interface.d.ts +3 -3
- package/{interfaces → src/interfaces}/path-value-set.interface.d.ts +12 -12
- package/{types → src/types}/deep-partial.type.d.ts +6 -0
- package/{types → src/types}/literal-object.type.d.ts +2 -2
- package/{utils → src/utils}/index.d.ts +1 -0
- package/{utils → src/utils}/object.utils.d.ts +23 -19
- package/CHANGELOG.md +0 -19
- package/README.md +0 -7944
- package/constants/constants.js +0 -227
- package/constants/constants.js.map +0 -1
- package/constants/english-inflection-rules.js +0 -360
- package/constants/english-inflection-rules.js.map +0 -1
- package/constants/index.js +0 -6
- package/constants/index.js.map +0 -1
- package/enums/index.js +0 -5
- package/enums/index.js.map +0 -1
- package/enums/template-tag.enum.js +0 -39
- package/enums/template-tag.enum.js.map +0 -1
- package/index.js +0 -9
- package/index.js.map +0 -1
- package/interfaces/index.js +0 -7
- package/interfaces/index.js.map +0 -1
- package/interfaces/infinite-object.interface.js +0 -3
- package/interfaces/infinite-object.interface.js.map +0 -1
- package/interfaces/inflection-rule.interfaces.js +0 -3
- package/interfaces/inflection-rule.interfaces.js.map +0 -1
- package/interfaces/path-value-set.interface.js +0 -3
- package/interfaces/path-value-set.interface.js.map +0 -1
- package/readme-top.md +0 -187
- package/types/deep-partial.type.js +0 -3
- package/types/deep-partial.type.js.map +0 -1
- package/types/index.js +0 -38
- package/types/index.js.map +0 -1
- package/types/is-already-in-path.type.js +0 -3
- package/types/is-already-in-path.type.js.map +0 -1
- package/types/is-empty.type.js +0 -3
- package/types/is-empty.type.js.map +0 -1
- package/types/is-primitive.type.js +0 -4
- package/types/is-primitive.type.js.map +0 -1
- package/types/literal-object.type.js +0 -3
- package/types/literal-object.type.js.map +0 -1
- package/types/loose-autocomplete.type.js +0 -3
- package/types/loose-autocomplete.type.js.map +0 -1
- package/types/partial-with-null.type.js +0 -3
- package/types/partial-with-null.type.js.map +0 -1
- package/types/prettify.type.js +0 -3
- package/types/prettify.type.js.map +0 -1
- package/types/type.type.js +0 -3
- package/types/type.type.js.map +0 -1
- package/utils/assertions.utils.js +0 -163
- package/utils/assertions.utils.js.map +0 -1
- package/utils/file.utils.js +0 -1315
- package/utils/file.utils.js.map +0 -1
- package/utils/index.js +0 -9
- package/utils/index.js.map +0 -1
- package/utils/object.utils.js +0 -1069
- package/utils/object.utils.js.map +0 -1
- package/utils/string-template.utils.js +0 -269
- package/utils/string-template.utils.js.map +0 -1
- package/utils/string.utils.js +0 -1255
- package/utils/string.utils.js.map +0 -1
- package/utils/url.utils.js +0 -112
- package/utils/url.utils.js.map +0 -1
- /package/{constants → src/constants}/constants.d.ts +0 -0
- /package/{constants → src/constants}/english-inflection-rules.d.ts +0 -0
- /package/{constants → src/constants}/index.d.ts +0 -0
- /package/{enums → src/enums}/index.d.ts +0 -0
- /package/{enums → src/enums}/template-tag.enum.d.ts +0 -0
- /package/{interfaces → src/interfaces}/index.d.ts +0 -0
- /package/{interfaces → src/interfaces}/inflection-rule.interfaces.d.ts +0 -0
- /package/{types → src/types}/index.d.ts +0 -0
- /package/{types → src/types}/is-already-in-path.type.d.ts +0 -0
- /package/{types → src/types}/is-empty.type.d.ts +0 -0
- /package/{types → src/types}/is-primitive.type.d.ts +0 -0
- /package/{types → src/types}/loose-autocomplete.type.d.ts +0 -0
- /package/{types → src/types}/partial-with-null.type.d.ts +0 -0
- /package/{types → src/types}/prettify.type.d.ts +0 -0
- /package/{types → src/types}/type.type.d.ts +0 -0
- /package/{utils → src/utils}/assertions.utils.d.ts +0 -0
- /package/{utils → src/utils}/file.utils.d.ts +0 -0
- /package/{utils → src/utils}/string-template.utils.d.ts +0 -0
- /package/{utils → src/utils}/string.utils.d.ts +0 -0
- /package/{utils → src/utils}/url.utils.d.ts +0 -0
package/utils/object.utils.js
DELETED
|
@@ -1,1069 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
-
// noinspection JSUnusedGlobalSymbols
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.prune = exports.omit = exports.getValueByPath = exports.searchMapValues = exports.groupBy = exports.getMapKeys = void 0;
|
|
6
|
-
exports.deepCopy = deepCopy;
|
|
7
|
-
exports.getMapKey = getMapKey;
|
|
8
|
-
exports.objectToPathValueSet = objectToPathValueSet;
|
|
9
|
-
exports.pathValueSetToObject = pathValueSetToObject;
|
|
10
|
-
exports.hasOwnAll = hasOwnAll;
|
|
11
|
-
exports.getEnumValues = getEnumValues;
|
|
12
|
-
/**
|
|
13
|
-
* Deep copy an object.
|
|
14
|
-
* @template T Type of the object.
|
|
15
|
-
* @param {T} obj Object to copy.
|
|
16
|
-
* @returns {T} Copied object.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```TypeScript
|
|
20
|
-
* // Example usage
|
|
21
|
-
* const object = {
|
|
22
|
-
* name: "John Doe"
|
|
23
|
-
* }
|
|
24
|
-
*
|
|
25
|
-
* const copiedObject = deepCopy(object);
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
function deepCopy(obj) {
|
|
29
|
-
if (Array.isArray(obj)) {
|
|
30
|
-
return obj.map(deepCopy);
|
|
31
|
-
}
|
|
32
|
-
else if (typeof obj === "object" && obj !== null) {
|
|
33
|
-
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, deepCopy(value)]));
|
|
34
|
-
}
|
|
35
|
-
return obj;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Get the key of a map by value.
|
|
39
|
-
* @param {Map<string, unknown>} map Map to get key from.
|
|
40
|
-
* @param {unknown} value Value to get key for.
|
|
41
|
-
* @returns {string | undefined} Key of the map.
|
|
42
|
-
*
|
|
43
|
-
* @example
|
|
44
|
-
* ```TypeScript
|
|
45
|
-
* // Example usage
|
|
46
|
-
* const user = new Map<string, string>([
|
|
47
|
-
* ["firstName", "John"],
|
|
48
|
-
* ["lastName", "Doe"],
|
|
49
|
-
* ["preferredName", "John"],
|
|
50
|
-
* ["age", 30],
|
|
51
|
-
* ]);
|
|
52
|
-
*
|
|
53
|
-
* const key = getMapKey(user, "value2");
|
|
54
|
-
*
|
|
55
|
-
* // Example output: "firstName"
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
function getMapKey(map, value) {
|
|
59
|
-
return [...Array.from(map.entries())].find(([, v]) => v === value)?.[0];
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Retrieves all keys from a Map where the corresponding values contain a specified substring.
|
|
63
|
-
*
|
|
64
|
-
* This utility function searches through a Map's values and returns an array of keys
|
|
65
|
-
* whose associated values include the provided partial string. The search is case-sensitive
|
|
66
|
-
* and uses JavaScript's native string.includes() method for matching.
|
|
67
|
-
*
|
|
68
|
-
* This is particularly useful for implementing search functionality, filtering operations,
|
|
69
|
-
* or finding related entries in configuration maps, user preference stores, or any
|
|
70
|
-
* key-value data structure where you need to locate entries by partial value matching.
|
|
71
|
-
*
|
|
72
|
-
* @param {Map<string, string>} map - The Map to search through for matching values
|
|
73
|
-
* @param {string} partialValue - The substring to search for within the Map's values
|
|
74
|
-
* @returns {string[]} An array of keys whose corresponding values contain the partial value
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```typescript
|
|
78
|
-
* // Search user preferences for entries containing "John"
|
|
79
|
-
* const userPreferences = new Map<string, string>([
|
|
80
|
-
* ["displayName", "John Doe"],
|
|
81
|
-
* ["firstName", "John"],
|
|
82
|
-
* ["lastName", "Doe"],
|
|
83
|
-
* ["preferredName", "Johnny"],
|
|
84
|
-
* ["email", "john.doe@example.com"]
|
|
85
|
-
* ]);
|
|
86
|
-
*
|
|
87
|
-
* const johnKeys = getMapKeys(userPreferences, "John");
|
|
88
|
-
* // Returns: ["displayName", "firstName", "email"]
|
|
89
|
-
* ```
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* ```typescript
|
|
93
|
-
* // Find configuration keys related to database settings
|
|
94
|
-
* const config = new Map<string, string>([
|
|
95
|
-
* ["db_host", "localhost"],
|
|
96
|
-
* ["db_port", "5432"],
|
|
97
|
-
* ["cache_host", "redis-server"],
|
|
98
|
-
* ["db_name", "myapp_database"],
|
|
99
|
-
* ["log_level", "debug"]
|
|
100
|
-
* ]);
|
|
101
|
-
*
|
|
102
|
-
* const dbKeys = getMapKeys(config, "db");
|
|
103
|
-
* // Returns: ["db_name"] (only values containing "db", not keys)
|
|
104
|
-
* ```
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* ```typescript
|
|
108
|
-
* // Search in a translations map
|
|
109
|
-
* const translations = new Map<string, string>([
|
|
110
|
-
* ["welcome_message", "Welcome to our application"],
|
|
111
|
-
* ["error_message", "An error occurred"],
|
|
112
|
-
* ["success_message", "Operation completed successfully"],
|
|
113
|
-
* ["info_message", "Please check your information"]
|
|
114
|
-
* ]);
|
|
115
|
-
*
|
|
116
|
-
* const messageKeys = getMapKeys(translations, "message");
|
|
117
|
-
* // Returns: ["welcome_message", "error_message", "success_message", "info_message"]
|
|
118
|
-
* ```
|
|
119
|
-
*
|
|
120
|
-
* @remarks
|
|
121
|
-
* - The search is case-sensitive; "John" will not match "john"
|
|
122
|
-
* - Returns an empty array if no matches are found
|
|
123
|
-
* - The function iterates through all Map entries, so performance scales with Map size
|
|
124
|
-
* - Only works with Maps that have string values; other value types will cause runtime errors
|
|
125
|
-
*/
|
|
126
|
-
const getMapKeys = (map, partialValue) => {
|
|
127
|
-
const keys = [];
|
|
128
|
-
for (const [key, value] of Array.from(map.entries())) {
|
|
129
|
-
if (value.includes(partialValue)) {
|
|
130
|
-
keys.push(key);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return keys;
|
|
134
|
-
};
|
|
135
|
-
exports.getMapKeys = getMapKeys;
|
|
136
|
-
/**
|
|
137
|
-
* Groups an array of objects into a Map based on a key extraction function.
|
|
138
|
-
*
|
|
139
|
-
* This utility function takes an array of objects and organizes them into groups
|
|
140
|
-
* based on a common key or property. The grouping is performed using a key extraction
|
|
141
|
-
* function that you provide, which determines how each object should be categorized.
|
|
142
|
-
* The result is a Map where each key represents a group and the value is an array
|
|
143
|
-
* of objects belonging to that group.
|
|
144
|
-
*
|
|
145
|
-
* This is particularly useful for data analysis, reporting, organizing collections
|
|
146
|
-
* for display purposes, or preparing data for further processing where items need
|
|
147
|
-
* to be categorized by shared characteristics.
|
|
148
|
-
*
|
|
149
|
-
* @template K - The type of the grouping key (can be string, number, boolean, etc.)
|
|
150
|
-
* @template V - The type of the objects being grouped
|
|
151
|
-
* @param {Array<V>} list - The array of objects to group
|
|
152
|
-
* @param {(input: V) => K} keyGetter - A function that extracts the grouping key from each object.
|
|
153
|
-
* This function receives an object and should return the value
|
|
154
|
-
* to group by. If the function returns null or undefined,
|
|
155
|
-
* the object will be grouped under a null key.
|
|
156
|
-
* @returns {Map<K | null, Array<V>>} A Map where keys are the grouping values and values are arrays
|
|
157
|
-
* of objects that share that grouping key
|
|
158
|
-
*
|
|
159
|
-
* @example
|
|
160
|
-
* ```typescript
|
|
161
|
-
* // Group users by age
|
|
162
|
-
* interface User {
|
|
163
|
-
* name: string;
|
|
164
|
-
* age: number;
|
|
165
|
-
* department: string;
|
|
166
|
-
* }
|
|
167
|
-
*
|
|
168
|
-
* const users: User[] = [
|
|
169
|
-
* { name: "John", age: 30, department: "Engineering" },
|
|
170
|
-
* { name: "Jane", age: 25, department: "Marketing" },
|
|
171
|
-
* { name: "Bob", age: 30, department: "Engineering" },
|
|
172
|
-
* { name: "Alice", age: 25, department: "Sales" },
|
|
173
|
-
* { name: "Charlie", age: 35, department: "Engineering" }
|
|
174
|
-
* ];
|
|
175
|
-
*
|
|
176
|
-
* const usersByAge = groupBy(users, user => user.age);
|
|
177
|
-
* // Returns:
|
|
178
|
-
* // Map {
|
|
179
|
-
* // 30 => [
|
|
180
|
-
* // { name: "John", age: 30, department: "Engineering" },
|
|
181
|
-
* // { name: "Bob", age: 30, department: "Engineering" }
|
|
182
|
-
* // ],
|
|
183
|
-
* // 25 => [
|
|
184
|
-
* // { name: "Jane", age: 25, department: "Marketing" },
|
|
185
|
-
* // { name: "Alice", age: 25, department: "Sales" }
|
|
186
|
-
* // ],
|
|
187
|
-
* // 35 => [
|
|
188
|
-
* // { name: "Charlie", age: 35, department: "Engineering" }
|
|
189
|
-
* // ]
|
|
190
|
-
* // }
|
|
191
|
-
* ```
|
|
192
|
-
*
|
|
193
|
-
* @example
|
|
194
|
-
* ```typescript
|
|
195
|
-
* // Group products by category
|
|
196
|
-
* interface Product {
|
|
197
|
-
* id: number;
|
|
198
|
-
* name: string;
|
|
199
|
-
* category: string;
|
|
200
|
-
* price: number;
|
|
201
|
-
* }
|
|
202
|
-
*
|
|
203
|
-
* const products: Product[] = [
|
|
204
|
-
* { id: 1, name: "Laptop", category: "Electronics", price: 999 },
|
|
205
|
-
* { id: 2, name: "Book", category: "Education", price: 29 },
|
|
206
|
-
* { id: 3, name: "Phone", category: "Electronics", price: 699 },
|
|
207
|
-
* { id: 4, name: "Pen", category: "Office", price: 5 }
|
|
208
|
-
* ];
|
|
209
|
-
*
|
|
210
|
-
* const productsByCategory = groupBy(products, product => product.category);
|
|
211
|
-
* // Useful for creating category-based product listings
|
|
212
|
-
* ```
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* ```typescript
|
|
216
|
-
* // Group tasks by completion status with boolean keys
|
|
217
|
-
* interface Task {
|
|
218
|
-
* id: number;
|
|
219
|
-
* title: string;
|
|
220
|
-
* completed: boolean;
|
|
221
|
-
* }
|
|
222
|
-
*
|
|
223
|
-
* const tasks: Task[] = [
|
|
224
|
-
* { id: 1, title: "Review code", completed: true },
|
|
225
|
-
* { id: 2, title: "Write tests", completed: false },
|
|
226
|
-
* { id: 3, title: "Deploy app", completed: true },
|
|
227
|
-
* { id: 4, title: "Update docs", completed: false }
|
|
228
|
-
* ];
|
|
229
|
-
*
|
|
230
|
-
* const tasksByStatus = groupBy(tasks, task => task.completed);
|
|
231
|
-
* // Returns Map with boolean keys: true and false
|
|
232
|
-
* ```
|
|
233
|
-
*
|
|
234
|
-
* @remarks
|
|
235
|
-
* - The function preserves the original order of objects within each group
|
|
236
|
-
* - If the keyGetter function returns null or undefined, those objects will be grouped under a null key
|
|
237
|
-
* - The Map structure allows for efficient lookups and iteration over groups
|
|
238
|
-
* - Works with any type of grouping key (string, number, boolean, objects, etc.)
|
|
239
|
-
* - Empty arrays will return an empty Map
|
|
240
|
-
*/
|
|
241
|
-
const groupBy = (list, keyGetter) => {
|
|
242
|
-
const map = new Map();
|
|
243
|
-
list.forEach(item => {
|
|
244
|
-
const key = keyGetter(item);
|
|
245
|
-
const collection = map.get(key);
|
|
246
|
-
if (!collection) {
|
|
247
|
-
map.set(key, [item]);
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
collection.push(item);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
return map;
|
|
254
|
-
};
|
|
255
|
-
exports.groupBy = groupBy;
|
|
256
|
-
/**
|
|
257
|
-
* Retrieves all values from a Map that contain a specified substring.
|
|
258
|
-
*
|
|
259
|
-
* This utility function searches through a Map's values and returns an array of all values
|
|
260
|
-
* that include the provided partial string. The search is case-sensitive and uses JavaScript's
|
|
261
|
-
* native string.includes() method for matching. This is the counterpart to getMapKeys(),
|
|
262
|
-
* but instead of returning the keys, it returns the actual values that match.
|
|
263
|
-
*
|
|
264
|
-
* This is particularly useful for implementing search functionality where you need to find
|
|
265
|
-
* and display matching content, filtering data for autocomplete features, or extracting
|
|
266
|
-
* related values from configuration maps, user data, or any key-value store where you
|
|
267
|
-
* need to locate entries by partial content matching.
|
|
268
|
-
*
|
|
269
|
-
* @param {Map<string, string>} map - The Map to search through for matching values
|
|
270
|
-
* @param {string} partialValue - The substring to search for within the Map's values
|
|
271
|
-
* @returns {string[]} An array of values that contain the specified partial value
|
|
272
|
-
*
|
|
273
|
-
* @example
|
|
274
|
-
* ```typescript
|
|
275
|
-
* // Search user information for values containing "John"
|
|
276
|
-
* const userInfo = new Map<string, string>([
|
|
277
|
-
* ["fullName", "John Doe"],
|
|
278
|
-
* ["firstName", "John"],
|
|
279
|
-
* ["lastName", "Doe"],
|
|
280
|
-
* ["nickname", "Johnny"],
|
|
281
|
-
* ["email", "john.doe@example.com"],
|
|
282
|
-
* ["phone", "555-0123"]
|
|
283
|
-
* ]);
|
|
284
|
-
*
|
|
285
|
-
* const johnValues = searchMapValues(userInfo, "John");
|
|
286
|
-
* // Returns: ["John Doe", "John", "Johnny", "john.doe@example.com"]
|
|
287
|
-
* ```
|
|
288
|
-
*
|
|
289
|
-
* @example
|
|
290
|
-
* ```typescript
|
|
291
|
-
* // Find error messages containing specific keywords
|
|
292
|
-
* const errorMessages = new Map<string, string>([
|
|
293
|
-
* ["auth_failed", "Authentication failed. Please check your credentials."],
|
|
294
|
-
* ["network_error", "Network connection failed. Please try again."],
|
|
295
|
-
* ["validation_error", "Validation failed for the provided data."],
|
|
296
|
-
* ["timeout_error", "Request timeout. The server took too long to respond."],
|
|
297
|
-
* ["permission_denied", "Access denied. You don't have permission."]
|
|
298
|
-
* ]);
|
|
299
|
-
*
|
|
300
|
-
* const failedMessages = searchMapValues(errorMessages, "failed");
|
|
301
|
-
* // Returns: ["Authentication failed. Please check your credentials.",
|
|
302
|
-
* // "Network connection failed. Please try again.",
|
|
303
|
-
* // "Validation failed for the provided data."]
|
|
304
|
-
* ```
|
|
305
|
-
*
|
|
306
|
-
* @example
|
|
307
|
-
* ```typescript
|
|
308
|
-
* // Search configuration values for environment-specific settings
|
|
309
|
-
* const config = new Map<string, string>([
|
|
310
|
-
* ["api_url", "https://api.production.example.com"],
|
|
311
|
-
* ["db_host", "production-db.example.com"],
|
|
312
|
-
* ["cache_url", "redis://production-cache.example.com"],
|
|
313
|
-
* ["log_level", "error"],
|
|
314
|
-
* ["debug_mode", "false"]
|
|
315
|
-
* ]);
|
|
316
|
-
*
|
|
317
|
-
* const productionValues = searchMapValues(config, "production");
|
|
318
|
-
* // Returns: ["https://api.production.example.com",
|
|
319
|
-
* // "production-db.example.com",
|
|
320
|
-
* // "redis://production-cache.example.com"]
|
|
321
|
-
* ```
|
|
322
|
-
*
|
|
323
|
-
* @remarks
|
|
324
|
-
* - The search is case-sensitive; "John" will not match "john"
|
|
325
|
-
* - Returns an empty array if no matches are found
|
|
326
|
-
* - The function iterates through all Map entries, so performance scales with Map size
|
|
327
|
-
* - Only works with Maps that have string values; other value types will cause runtime errors
|
|
328
|
-
* - Values are returned in the order they appear in the Map iteration
|
|
329
|
-
*
|
|
330
|
-
* @see {@link getMapKeys} Function to get keys whose values contain a substring
|
|
331
|
-
*/
|
|
332
|
-
const searchMapValues = (map, partialValue) => {
|
|
333
|
-
const values = [];
|
|
334
|
-
for (const [, value] of Array.from(map.entries())) {
|
|
335
|
-
if (value.includes(partialValue)) {
|
|
336
|
-
values.push(value);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
return values;
|
|
340
|
-
};
|
|
341
|
-
exports.searchMapValues = searchMapValues;
|
|
342
|
-
/**
|
|
343
|
-
* Gets a value from a nested object using a dot-notation path string.
|
|
344
|
-
*
|
|
345
|
-
* This function safely traverses a deeply nested object structure using a string path
|
|
346
|
-
* with dot notation. It handles both object properties and array indices within the path.
|
|
347
|
-
*
|
|
348
|
-
* @template T - Type of the value to be returned.
|
|
349
|
-
* @param {InfiniteObject} obj - The object to retrieve the value from.
|
|
350
|
-
* @param {string} path - The dot-notation path to the desired value.
|
|
351
|
-
* - Use dots to navigate through nested objects: 'user.profile.address'
|
|
352
|
-
* - Use array notation for accessing array elements: 'items[0]' or 'users[2].name'
|
|
353
|
-
* @returns {T | undefined} - The value at the specified path, or undefined if:
|
|
354
|
-
* - Any part of the path doesn't exist
|
|
355
|
-
* - An array index is out of bounds
|
|
356
|
-
* - The path format is invalid
|
|
357
|
-
*
|
|
358
|
-
* @example
|
|
359
|
-
* ```typescript
|
|
360
|
-
* // Simple nested object property
|
|
361
|
-
* const user = {
|
|
362
|
-
* profile: {
|
|
363
|
-
* name: "John Doe",
|
|
364
|
-
* contact: { email: "john@example.com" }
|
|
365
|
-
* }
|
|
366
|
-
* };
|
|
367
|
-
* const email = getValueByPath<string>(user, "profile.contact.email");
|
|
368
|
-
* // Returns: "john@example.com"
|
|
369
|
-
* ```
|
|
370
|
-
*
|
|
371
|
-
* @example
|
|
372
|
-
* ```typescript
|
|
373
|
-
* // Accessing array elements
|
|
374
|
-
* const data = {
|
|
375
|
-
* users: [
|
|
376
|
-
* { id: 1, name: "Alice" },
|
|
377
|
-
* { id: 2, name: "Bob" }
|
|
378
|
-
* ]
|
|
379
|
-
* };
|
|
380
|
-
* const name = getValueByPath<string>(data, "users[1].name");
|
|
381
|
-
* // Returns: "Bob"
|
|
382
|
-
* ```
|
|
383
|
-
*/
|
|
384
|
-
const getValueByPath = (obj, path) => {
|
|
385
|
-
const keys = path.split("."); // Split the path into an array of keys
|
|
386
|
-
let value = obj;
|
|
387
|
-
for (const key of keys) {
|
|
388
|
-
// noinspection RegExpRedundantEscape
|
|
389
|
-
const regExp = /^(\w+)\[(\d+)\]$/;
|
|
390
|
-
const isArrayIndex = regExp.exec(key); // Check if the key is an array index
|
|
391
|
-
if (isArrayIndex) {
|
|
392
|
-
const arrayKey = isArrayIndex[1];
|
|
393
|
-
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
|
|
394
|
-
const index = Number(isArrayIndex[2]);
|
|
395
|
-
if (arrayKey && Array.isArray(value[arrayKey]) && index >= 0 && index < value[arrayKey].length) {
|
|
396
|
-
value = value[arrayKey][index]; // Update the value to the array element
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
return undefined; // Return undefined if the array index is invalid
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
else if (value && typeof value === "object" && key in value) {
|
|
403
|
-
value = value[key]; // Update the value to the nested property
|
|
404
|
-
}
|
|
405
|
-
else {
|
|
406
|
-
return undefined; // Return undefined if any key is not found
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return value;
|
|
410
|
-
};
|
|
411
|
-
exports.getValueByPath = getValueByPath;
|
|
412
|
-
/**
|
|
413
|
-
* Converts a nested object into a flattened PathValueSet representation.
|
|
414
|
-
*
|
|
415
|
-
* This function transforms a hierarchical object structure into a flat key-value map
|
|
416
|
-
* where keys represent paths to values in the original object using dot notation.
|
|
417
|
-
*
|
|
418
|
-
* The function recursively traverses the object and flattens nested properties,
|
|
419
|
-
* converting object hierarchies like `{ user: { name: 'John' } }` into
|
|
420
|
-
* path-based entries like `{ 'user.name': 'John' }`.
|
|
421
|
-
*
|
|
422
|
-
* @param {LiteralObject} obj - The nested object to flatten
|
|
423
|
-
* @returns {PathValueSet} - A flattened representation where:
|
|
424
|
-
* - Keys are dot-notation paths to values in the original object
|
|
425
|
-
* - Values are primitive values (strings, numbers, booleans) from the original object
|
|
426
|
-
*
|
|
427
|
-
* @remarks
|
|
428
|
-
* - Array values will be preserved as-is (not flattened into separate paths)
|
|
429
|
-
* - Only primitive values (string, number, boolean) are supported as leaf values
|
|
430
|
-
* - Circular references are not handled and will cause a stack overflow
|
|
431
|
-
*
|
|
432
|
-
* @see {@link pathValueSetToObject} The inverse operation to convert a PathValueSet back to a nested object
|
|
433
|
-
* @see {@link PathValueSet} The interface for the returned flattened object
|
|
434
|
-
*
|
|
435
|
-
* @example
|
|
436
|
-
* ```typescript
|
|
437
|
-
* // Flatten a nested user object
|
|
438
|
-
* const user = {
|
|
439
|
-
* id: 123,
|
|
440
|
-
* name: "John Doe",
|
|
441
|
-
* isActive: true,
|
|
442
|
-
* profile: {
|
|
443
|
-
* age: 30,
|
|
444
|
-
* address: {
|
|
445
|
-
* city: "New York",
|
|
446
|
-
* zip: "10001"
|
|
447
|
-
* }
|
|
448
|
-
* }
|
|
449
|
-
* };
|
|
450
|
-
*
|
|
451
|
-
* const flattened = objectToPathValueSet(user);
|
|
452
|
-
*
|
|
453
|
-
* // Result:
|
|
454
|
-
* // {
|
|
455
|
-
* // "id": 123,
|
|
456
|
-
* // "name": "John Doe",
|
|
457
|
-
* // "isActive": true,
|
|
458
|
-
* // "profile.age": 30,
|
|
459
|
-
* // "profile.address.city": "New York",
|
|
460
|
-
* // "profile.address.zip": "10001"
|
|
461
|
-
* // }
|
|
462
|
-
* ```
|
|
463
|
-
*/
|
|
464
|
-
function objectToPathValueSet(obj) {
|
|
465
|
-
const result = {};
|
|
466
|
-
function traverse(obj, path = []) {
|
|
467
|
-
for (const key in obj) {
|
|
468
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
469
|
-
const value = obj[key];
|
|
470
|
-
if (typeof value === "object" && !Array.isArray(value)) {
|
|
471
|
-
traverse(value, [...path, key]);
|
|
472
|
-
}
|
|
473
|
-
else {
|
|
474
|
-
result[[...path, key].join(".")] = value;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
traverse(obj);
|
|
480
|
-
return result;
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* Converts a flattened PathValueSet back into a nested object structure.
|
|
484
|
-
*
|
|
485
|
-
* This function is the inverse of `objectToPathValueSet`. It takes a flat map of
|
|
486
|
-
* dot-notation paths to values and reconstructs a hierarchical object structure.
|
|
487
|
-
*
|
|
488
|
-
* Each key in the input PathValueSet represents a path through the object hierarchy,
|
|
489
|
-
* with dots separating each level. The function builds a nested object structure by
|
|
490
|
-
* parsing these paths and placing values at the appropriate locations.
|
|
491
|
-
*
|
|
492
|
-
* @template R - The type of the returned object (defaults to object)
|
|
493
|
-
* @param {PathValueSet} pathValueSet - A flattened object with dot-notation path keys
|
|
494
|
-
* @returns {R} - A reconstructed nested object with the original hierarchy
|
|
495
|
-
*
|
|
496
|
-
* @remarks
|
|
497
|
-
* - Paths are validated for safety to prevent injection attacks
|
|
498
|
-
* - Invalid paths are silently skipped (not included in the result)
|
|
499
|
-
* - Path components should contain only alphanumeric characters, underscores, hyphens, and dots
|
|
500
|
-
*
|
|
501
|
-
* @see {@link objectToPathValueSet} The inverse operation to convert an object to PathValueSet
|
|
502
|
-
* @see {@link PathValueSet} The interface for the input flattened object
|
|
503
|
-
*
|
|
504
|
-
* @example
|
|
505
|
-
* ```typescript
|
|
506
|
-
* // Convert a flat PathValueSet to a nested object
|
|
507
|
-
* const flatData = {
|
|
508
|
-
* "id": 123,
|
|
509
|
-
* "name": "John Doe",
|
|
510
|
-
* "profile.age": 30,
|
|
511
|
-
* "profile.address.city": "New York",
|
|
512
|
-
* "profile.address.zip": "10001"
|
|
513
|
-
* };
|
|
514
|
-
*
|
|
515
|
-
* const nestedObject = pathValueSetToObject(flatData);
|
|
516
|
-
*
|
|
517
|
-
* Result:
|
|
518
|
-
* // {
|
|
519
|
-
* // id: 123,
|
|
520
|
-
* // name: "John Doe",
|
|
521
|
-
* // profile: {
|
|
522
|
-
* // age: 30,
|
|
523
|
-
* // address: {
|
|
524
|
-
* // city: "New York",
|
|
525
|
-
* // zip: "10001"
|
|
526
|
-
* // }
|
|
527
|
-
* // }
|
|
528
|
-
* // }
|
|
529
|
-
* ```
|
|
530
|
-
*
|
|
531
|
-
* @example
|
|
532
|
-
* ```typescript
|
|
533
|
-
* // Typed return value
|
|
534
|
-
* interface User {
|
|
535
|
-
* id: number;
|
|
536
|
-
* name: string;
|
|
537
|
-
* profile: {
|
|
538
|
-
* age: number;
|
|
539
|
-
* address: {
|
|
540
|
-
* city: string;
|
|
541
|
-
* zip: string;
|
|
542
|
-
* };
|
|
543
|
-
* };
|
|
544
|
-
* }
|
|
545
|
-
*
|
|
546
|
-
* const userData = pathValueSetToObject<User>(flatData);
|
|
547
|
-
* // Returns object with User type
|
|
548
|
-
* ```
|
|
549
|
-
*/
|
|
550
|
-
function pathValueSetToObject(pathValueSet) {
|
|
551
|
-
const object = {};
|
|
552
|
-
// Helper function to validate paths
|
|
553
|
-
const isValidPath = (path) => {
|
|
554
|
-
const regex = /^[a-zA-Z0-9_.-]+$/;
|
|
555
|
-
return path.split(".").every(part => regex.test(part));
|
|
556
|
-
};
|
|
557
|
-
// Helper function to set nested properties
|
|
558
|
-
const setObjectValue = (obj, keys, value) => {
|
|
559
|
-
const [firstKey, ...remainingKeys] = keys;
|
|
560
|
-
if (remainingKeys.length === 0) {
|
|
561
|
-
obj[firstKey] = value; // Set value at the final key
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
// Initialize the key if it doesn't exist
|
|
565
|
-
obj[firstKey] = obj[firstKey] || {};
|
|
566
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
567
|
-
setObjectValue(obj[firstKey], remainingKeys, value); // Recurse for the rest of the keys
|
|
568
|
-
// TODO: v2.0 Fix type for above
|
|
569
|
-
};
|
|
570
|
-
for (const path in pathValueSet) {
|
|
571
|
-
if (Object.prototype.hasOwnProperty.call(pathValueSet, path)) {
|
|
572
|
-
if (!isValidPath(path)) {
|
|
573
|
-
continue; // Skip invalid paths
|
|
574
|
-
}
|
|
575
|
-
const value = pathValueSet[path];
|
|
576
|
-
const keys = path.split("."); // Split path into keys
|
|
577
|
-
setObjectValue(object, keys, value); // Use helper to populate the object
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
return object;
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* Creates a new object by omitting specified properties and undefined values from the original object.
|
|
584
|
-
*
|
|
585
|
-
* This utility function performs a selective copy of an object, excluding both explicitly
|
|
586
|
-
* specified properties and any properties with undefined values. The function modifies
|
|
587
|
-
* the original object in-place by deleting the unwanted properties, making it useful for
|
|
588
|
-
* cleaning up objects before serialization, API calls, or data processing.
|
|
589
|
-
*
|
|
590
|
-
* This is particularly useful for preparing data for API requests where you need to remove
|
|
591
|
-
* sensitive fields, clean up form data, or exclude certain properties from being sent to
|
|
592
|
-
* external services. It's also helpful for removing undefined values that might cause
|
|
593
|
-
* issues in JSON serialization or database operations.
|
|
594
|
-
*
|
|
595
|
-
* @template T - The type of the object being processed
|
|
596
|
-
* @param {Partial<T>} obj - The object to process and remove properties from.
|
|
597
|
-
* This object will be modified in-place.
|
|
598
|
-
* @param {(keyof T)[]} [keys] - Optional array of property keys to omit from the object.
|
|
599
|
-
* If not provided, only undefined properties will be removed.
|
|
600
|
-
* @returns {Partial<T>} The same object reference with specified properties and undefined values removed
|
|
601
|
-
*
|
|
602
|
-
* @example
|
|
603
|
-
* ```typescript
|
|
604
|
-
* // Remove specific properties and undefined values
|
|
605
|
-
* interface User {
|
|
606
|
-
* id: number;
|
|
607
|
-
* name: string;
|
|
608
|
-
* email: string;
|
|
609
|
-
* role: string;
|
|
610
|
-
* address?: string;
|
|
611
|
-
* phone?: string;
|
|
612
|
-
* }
|
|
613
|
-
*
|
|
614
|
-
* const user: Partial<User> = {
|
|
615
|
-
* id: 123,
|
|
616
|
-
* name: "John Doe",
|
|
617
|
-
* email: "john@example.com",
|
|
618
|
-
* role: "admin",
|
|
619
|
-
* address: undefined,
|
|
620
|
-
* phone: "555-0123"
|
|
621
|
-
* };
|
|
622
|
-
*
|
|
623
|
-
* const cleanUser = omit(user, ["role"]);
|
|
624
|
-
* // Returns: { id: 123, name: "John Doe", email: "john@example.com", phone: "555-0123" }
|
|
625
|
-
* // Note: 'role' and 'address' (undefined) are removed
|
|
626
|
-
* ```
|
|
627
|
-
*
|
|
628
|
-
* @example
|
|
629
|
-
* ```typescript
|
|
630
|
-
* // Remove only undefined values (no specific keys)
|
|
631
|
-
* const formData = {
|
|
632
|
-
* firstName: "John",
|
|
633
|
-
* lastName: "Doe",
|
|
634
|
-
* middleName: undefined,
|
|
635
|
-
* email: "john@example.com",
|
|
636
|
-
* phone: undefined
|
|
637
|
-
* };
|
|
638
|
-
*
|
|
639
|
-
* const cleanFormData = omit(formData);
|
|
640
|
-
* // Returns: { firstName: "John", lastName: "Doe", email: "john@example.com" }
|
|
641
|
-
* ```
|
|
642
|
-
*
|
|
643
|
-
* @example
|
|
644
|
-
* ```typescript
|
|
645
|
-
* // Prepare data for API request by removing sensitive fields
|
|
646
|
-
* interface UserProfile {
|
|
647
|
-
* id: number;
|
|
648
|
-
* username: string;
|
|
649
|
-
* email: string;
|
|
650
|
-
* password: string;
|
|
651
|
-
* internalNotes: string;
|
|
652
|
-
* createdAt: Date;
|
|
653
|
-
* }
|
|
654
|
-
*
|
|
655
|
-
* const userProfile: Partial<UserProfile> = {
|
|
656
|
-
* id: 1,
|
|
657
|
-
* username: "johndoe",
|
|
658
|
-
* email: "john@example.com",
|
|
659
|
-
* password: "secret123",
|
|
660
|
-
* internalNotes: "VIP customer",
|
|
661
|
-
* createdAt: new Date()
|
|
662
|
-
* };
|
|
663
|
-
*
|
|
664
|
-
* const publicProfile = omit(userProfile, ["password", "internalNotes"]);
|
|
665
|
-
* // Safe to send to client: { id: 1, username: "johndoe", email: "john@example.com", createdAt: Date }
|
|
666
|
-
* ```
|
|
667
|
-
*
|
|
668
|
-
* @remarks
|
|
669
|
-
* - This function modifies the original object in-place rather than creating a copy
|
|
670
|
-
* - Both explicitly specified keys and undefined properties are removed
|
|
671
|
-
* - If the input object is falsy (null, undefined), it's returned as-is
|
|
672
|
-
* - The function uses delete operator, which may affect object performance in some JavaScript engines
|
|
673
|
-
* - Properties with null values are preserved (only undefined values are automatically removed)
|
|
674
|
-
*
|
|
675
|
-
* @see {@link prune} Function for more comprehensive object cleaning including null and empty values
|
|
676
|
-
*/
|
|
677
|
-
const omit = (obj, keys) => {
|
|
678
|
-
if (obj) {
|
|
679
|
-
Object.keys(obj).forEach(key => {
|
|
680
|
-
return (obj[key] === undefined || keys?.includes(key)) && delete obj[key];
|
|
681
|
-
});
|
|
682
|
-
}
|
|
683
|
-
return obj;
|
|
684
|
-
};
|
|
685
|
-
exports.omit = omit;
|
|
686
|
-
/**
|
|
687
|
-
* Creates a deep copy of an object with all empty, null, undefined, and optionally prototype properties removed.
|
|
688
|
-
*
|
|
689
|
-
* This utility function recursively traverses an object and creates a clean copy by excluding
|
|
690
|
-
* properties that are null, undefined, or empty strings. It performs a deep cleaning operation,
|
|
691
|
-
* processing nested objects recursively to ensure that the entire object hierarchy is pruned
|
|
692
|
-
* of unwanted values.
|
|
693
|
-
*
|
|
694
|
-
* This is particularly useful for preparing data for serialization, cleaning up API responses,
|
|
695
|
-
* removing empty form fields, or ensuring that only meaningful data is processed or stored.
|
|
696
|
-
* The function is especially valuable when working with deeply nested objects that may contain
|
|
697
|
-
* sparse data or when you need to eliminate all traces of empty values from complex data structures.
|
|
698
|
-
*
|
|
699
|
-
* @template T - The type of the object being pruned
|
|
700
|
-
* @param {PartialWithNull<T>} obj - The object to prune. Can contain null, undefined, or empty values.
|
|
701
|
-
* @param {boolean} [omitPrototype=false] - Whether to exclude inherited properties from the prototype chain.
|
|
702
|
-
* If true, only own properties will be included in the result.
|
|
703
|
-
* If false (default), inherited enumerable properties will be processed.
|
|
704
|
-
* @returns {T} A new object with all empty, null, and undefined properties recursively removed
|
|
705
|
-
*
|
|
706
|
-
* @example
|
|
707
|
-
* ```typescript
|
|
708
|
-
* // Clean up a user profile with nested empty values
|
|
709
|
-
* interface UserProfile {
|
|
710
|
-
* id: number;
|
|
711
|
-
* name: string;
|
|
712
|
-
* contact: {
|
|
713
|
-
* email: string;
|
|
714
|
-
* phone?: string;
|
|
715
|
-
* address?: {
|
|
716
|
-
* street: string;
|
|
717
|
-
* city: string;
|
|
718
|
-
* zip?: string;
|
|
719
|
-
* };
|
|
720
|
-
* };
|
|
721
|
-
* preferences: {
|
|
722
|
-
* theme: string;
|
|
723
|
-
* notifications?: boolean;
|
|
724
|
-
* };
|
|
725
|
-
* }
|
|
726
|
-
*
|
|
727
|
-
* const userProfile = {
|
|
728
|
-
* id: 123,
|
|
729
|
-
* name: "John Doe",
|
|
730
|
-
* contact: {
|
|
731
|
-
* email: "john@example.com",
|
|
732
|
-
* phone: "",
|
|
733
|
-
* address: {
|
|
734
|
-
* street: "123 Main St",
|
|
735
|
-
* city: "New York",
|
|
736
|
-
* zip: null
|
|
737
|
-
* }
|
|
738
|
-
* },
|
|
739
|
-
* preferences: {
|
|
740
|
-
* theme: "dark",
|
|
741
|
-
* notifications: undefined
|
|
742
|
-
* }
|
|
743
|
-
* };
|
|
744
|
-
*
|
|
745
|
-
* const cleanProfile = prune(userProfile);
|
|
746
|
-
* // Returns:
|
|
747
|
-
* // {
|
|
748
|
-
* // id: 123,
|
|
749
|
-
* // name: "John Doe",
|
|
750
|
-
* // contact: {
|
|
751
|
-
* // email: "john@example.com",
|
|
752
|
-
* // address: {
|
|
753
|
-
* // street: "123 Main St",
|
|
754
|
-
* // city: "New York"
|
|
755
|
-
* // }
|
|
756
|
-
* // },
|
|
757
|
-
* // preferences: {
|
|
758
|
-
* // theme: "dark"
|
|
759
|
-
* // }
|
|
760
|
-
* // }
|
|
761
|
-
* ```
|
|
762
|
-
*
|
|
763
|
-
* @example
|
|
764
|
-
* ```typescript
|
|
765
|
-
* // Clean up form data before submission
|
|
766
|
-
* const formData = {
|
|
767
|
-
* firstName: "John",
|
|
768
|
-
* lastName: "Doe",
|
|
769
|
-
* middleName: "",
|
|
770
|
-
* email: "john@example.com",
|
|
771
|
-
* phone: null,
|
|
772
|
-
* address: {
|
|
773
|
-
* street: "123 Main St",
|
|
774
|
-
* apartment: "",
|
|
775
|
-
* city: "New York",
|
|
776
|
-
* state: undefined,
|
|
777
|
-
* zip: "10001"
|
|
778
|
-
* },
|
|
779
|
-
* emergencyContact: {
|
|
780
|
-
* name: "",
|
|
781
|
-
* phone: null
|
|
782
|
-
* }
|
|
783
|
-
* };
|
|
784
|
-
*
|
|
785
|
-
* const cleanFormData = prune(formData);
|
|
786
|
-
* // Returns only fields with meaningful values:
|
|
787
|
-
* // {
|
|
788
|
-
* // firstName: "John",
|
|
789
|
-
* // lastName: "Doe",
|
|
790
|
-
* // email: "john@example.com",
|
|
791
|
-
* // address: {
|
|
792
|
-
* // street: "123 Main St",
|
|
793
|
-
* // city: "New York",
|
|
794
|
-
* // zip: "10001"
|
|
795
|
-
* // }
|
|
796
|
-
* // }
|
|
797
|
-
* // Note: emergencyContact object is completely removed as all its properties were empty
|
|
798
|
-
* ```
|
|
799
|
-
*
|
|
800
|
-
* @example
|
|
801
|
-
* ```typescript
|
|
802
|
-
* // Control prototype property inclusion
|
|
803
|
-
* class BaseUser {
|
|
804
|
-
* role = "user";
|
|
805
|
-
* }
|
|
806
|
-
*
|
|
807
|
-
* class ExtendedUser extends BaseUser {
|
|
808
|
-
* name = "John";
|
|
809
|
-
* email = "";
|
|
810
|
-
* }
|
|
811
|
-
*
|
|
812
|
-
* const user = new ExtendedUser();
|
|
813
|
-
*
|
|
814
|
-
* const prunedWithPrototype = prune(user, false);
|
|
815
|
-
* // Includes inherited 'role' property: { role: "user", name: "John" }
|
|
816
|
-
*
|
|
817
|
-
* const prunedOwnOnly = prune(user, true);
|
|
818
|
-
* // Only own properties: { name: "John" }
|
|
819
|
-
* ```
|
|
820
|
-
*
|
|
821
|
-
* @remarks
|
|
822
|
-
* - Creates a new object rather than modifying the original (unlike the omit function)
|
|
823
|
-
* - Recursively processes nested objects to ensure deep cleaning
|
|
824
|
-
* - Removes properties with values: null, undefined, or empty string ("")
|
|
825
|
-
* - Preserves properties with falsy but meaningful values like 0, false
|
|
826
|
-
* - If a nested object becomes empty after pruning, it will be excluded from the result
|
|
827
|
-
* - Non-object types are returned as an empty object of the target type
|
|
828
|
-
* - The function preserves the type structure while removing empty values
|
|
829
|
-
*
|
|
830
|
-
* @see {@link omit} Function for selective property removal without deep cleaning
|
|
831
|
-
*/
|
|
832
|
-
const prune = (obj, omitPrototype) => {
|
|
833
|
-
const objClone = {};
|
|
834
|
-
if (typeof obj !== "object") {
|
|
835
|
-
return objClone;
|
|
836
|
-
}
|
|
837
|
-
for (const key in obj) {
|
|
838
|
-
if (!omitPrototype || Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
839
|
-
if (obj[key] !== null && typeof obj[key] === "object") {
|
|
840
|
-
objClone[key] = (0, exports.prune)(obj[key], omitPrototype);
|
|
841
|
-
}
|
|
842
|
-
else if (obj[key] !== null && obj[key] !== undefined && obj[key] !== "") {
|
|
843
|
-
objClone[key] = obj[key];
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
return objClone;
|
|
848
|
-
};
|
|
849
|
-
exports.prune = prune;
|
|
850
|
-
/**
|
|
851
|
-
* Checks if an object has all specified properties as its own properties (not inherited).
|
|
852
|
-
*
|
|
853
|
-
* This utility function verifies that an object contains all the properties listed in the
|
|
854
|
-
* provided array as direct (own) properties, not inherited from the prototype chain.
|
|
855
|
-
* It uses the modern Object.hasOwn() method for reliable property existence checking,
|
|
856
|
-
* making it safer than using hasOwnProperty() directly.
|
|
857
|
-
*
|
|
858
|
-
* This is particularly useful for validating object structures, ensuring required properties
|
|
859
|
-
* exist before processing, implementing type guards, or verifying that objects conform to
|
|
860
|
-
* expected interfaces. It's commonly used in data validation, API request validation,
|
|
861
|
-
* and ensuring objects have all necessary properties before performing operations.
|
|
862
|
-
*
|
|
863
|
-
* @param {Record<PropertyKey, unknown>} obj - The object to check for property existence
|
|
864
|
-
* @param {PropertyKey[]} props - Array of property keys to verify exist on the object.
|
|
865
|
-
* PropertyKey includes string, number, and symbol types.
|
|
866
|
-
* @returns {boolean} True if the object has all specified properties as own properties, false otherwise
|
|
867
|
-
*
|
|
868
|
-
* @example
|
|
869
|
-
* ```typescript
|
|
870
|
-
* // Validate that a user object has required properties
|
|
871
|
-
* interface User {
|
|
872
|
-
* id: number;
|
|
873
|
-
* name: string;
|
|
874
|
-
* email: string;
|
|
875
|
-
* }
|
|
876
|
-
*
|
|
877
|
-
* const user = {
|
|
878
|
-
* id: 123,
|
|
879
|
-
* name: "John Doe",
|
|
880
|
-
* email: "john@example.com",
|
|
881
|
-
* role: "admin"
|
|
882
|
-
* };
|
|
883
|
-
*
|
|
884
|
-
* const hasRequiredProps = hasOwnAll(user, ["id", "name", "email"]);
|
|
885
|
-
* // Returns: true
|
|
886
|
-
*
|
|
887
|
-
* const hasAllProps = hasOwnAll(user, ["id", "name", "email", "phone"]);
|
|
888
|
-
* // Returns: false (missing 'phone' property)
|
|
889
|
-
* ```
|
|
890
|
-
*
|
|
891
|
-
* @example
|
|
892
|
-
* ```typescript
|
|
893
|
-
* // Validate API request payload
|
|
894
|
-
* function processUserUpdate(payload: unknown): User | null {
|
|
895
|
-
* if (typeof payload === 'object' && payload !== null) {
|
|
896
|
-
* const requiredFields = ['id', 'name', 'email'];
|
|
897
|
-
*
|
|
898
|
-
* if (hasOwnAll(payload as Record<string, unknown>, requiredFields)) {
|
|
899
|
-
* // Safe to process as we know all required fields exist
|
|
900
|
-
* return updateUser(payload as User);
|
|
901
|
-
* }
|
|
902
|
-
* }
|
|
903
|
-
*
|
|
904
|
-
* throw new Error('Invalid payload: missing required fields');
|
|
905
|
-
* }
|
|
906
|
-
* ```
|
|
907
|
-
*
|
|
908
|
-
* @example
|
|
909
|
-
* ```typescript
|
|
910
|
-
* // Check configuration object completeness
|
|
911
|
-
* interface DatabaseConfig {
|
|
912
|
-
* host: string;
|
|
913
|
-
* port: number;
|
|
914
|
-
* database: string;
|
|
915
|
-
* username: string;
|
|
916
|
-
* password: string;
|
|
917
|
-
* }
|
|
918
|
-
*
|
|
919
|
-
* const config = {
|
|
920
|
-
* host: "localhost",
|
|
921
|
-
* port: 5432,
|
|
922
|
-
* database: "myapp",
|
|
923
|
-
* username: "admin"
|
|
924
|
-
* // password is missing
|
|
925
|
-
* };
|
|
926
|
-
*
|
|
927
|
-
* const requiredConfigKeys = ['host', 'port', 'database', 'username', 'password'];
|
|
928
|
-
* const isConfigComplete = hasOwnAll(config, requiredConfigKeys);
|
|
929
|
-
* // Returns: false
|
|
930
|
-
*
|
|
931
|
-
* if (!isConfigComplete) {
|
|
932
|
-
* throw new Error('Database configuration is incomplete');
|
|
933
|
-
* }
|
|
934
|
-
* ```
|
|
935
|
-
*
|
|
936
|
-
* @example
|
|
937
|
-
* ```typescript
|
|
938
|
-
* // Work with symbol properties
|
|
939
|
-
* const symbolKey = Symbol('secret');
|
|
940
|
-
* const obj = {
|
|
941
|
-
* name: "test",
|
|
942
|
-
* [symbolKey]: "hidden value"
|
|
943
|
-
* };
|
|
944
|
-
*
|
|
945
|
-
* const hasSymbolProp = hasOwnAll(obj, ['name', symbolKey]);
|
|
946
|
-
* // Returns: true
|
|
947
|
-
* ```
|
|
948
|
-
*
|
|
949
|
-
* @remarks
|
|
950
|
-
* - Uses Object.hasOwn() which is more reliable than hasOwnProperty()
|
|
951
|
-
* - Only checks for own properties, not inherited properties from the prototype chain
|
|
952
|
-
* - Returns false immediately if any property is missing (short-circuit evaluation)
|
|
953
|
-
* - Works with string, number, and symbol property keys
|
|
954
|
-
* - Returns true for an empty properties array (vacuous truth)
|
|
955
|
-
* - Does not check property values, only existence
|
|
956
|
-
*
|
|
957
|
-
* @see {@link Object.hasOwn} The underlying method used for property checking
|
|
958
|
-
*/
|
|
959
|
-
function hasOwnAll(obj, props) {
|
|
960
|
-
return props.every((prop) => Object.hasOwn(obj, prop));
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Extracts all values from a TypeScript enum, handling both string and numeric enum types correctly.
|
|
964
|
-
*
|
|
965
|
-
* This utility function safely retrieves the actual values from TypeScript enums, accounting for
|
|
966
|
-
* the different internal representations of string and numeric enums. For numeric enums, TypeScript
|
|
967
|
-
* creates reverse mappings (value → key), so this function filters out the reverse mapping entries
|
|
968
|
-
* to return only the actual enum values. For string enums, it returns all values directly.
|
|
969
|
-
*
|
|
970
|
-
* This is particularly useful when you need to iterate over enum values, validate input against
|
|
971
|
-
* enum values, create dropdown options from enums, or perform any operation that requires access
|
|
972
|
-
* to the actual enum values rather than the keys.
|
|
973
|
-
*
|
|
974
|
-
* @template T - The enum type extending object
|
|
975
|
-
* @param {T} e - The enum object to extract values from
|
|
976
|
-
* @returns {T[keyof T][]} An array containing all the actual values of the enum
|
|
977
|
-
*
|
|
978
|
-
* @example
|
|
979
|
-
* ```typescript
|
|
980
|
-
* // String enum example
|
|
981
|
-
* enum Color {
|
|
982
|
-
* RED = "red",
|
|
983
|
-
* GREEN = "green",
|
|
984
|
-
* BLUE = "blue"
|
|
985
|
-
* }
|
|
986
|
-
*
|
|
987
|
-
* const colorValues = getEnumValues(Color);
|
|
988
|
-
* // Returns: ["red", "green", "blue"]
|
|
989
|
-
*
|
|
990
|
-
* // Use for validation
|
|
991
|
-
* function isValidColor(value: string): value is Color {
|
|
992
|
-
* return getEnumValues(Color).includes(value as Color);
|
|
993
|
-
* }
|
|
994
|
-
* ```
|
|
995
|
-
*
|
|
996
|
-
* @example
|
|
997
|
-
* ```typescript
|
|
998
|
-
* // Numeric enum example
|
|
999
|
-
* enum Status {
|
|
1000
|
-
* PENDING, // 0
|
|
1001
|
-
* APPROVED, // 1
|
|
1002
|
-
* REJECTED // 2
|
|
1003
|
-
* }
|
|
1004
|
-
*
|
|
1005
|
-
* const statusValues = getEnumValues(Status);
|
|
1006
|
-
* // Returns: [0, 1, 2] (not ["PENDING", "APPROVED", "REJECTED", 0, 1, 2])
|
|
1007
|
-
*
|
|
1008
|
-
* // Use for creating select options
|
|
1009
|
-
* const statusOptions = getEnumValues(Status).map(value => ({
|
|
1010
|
-
* value,
|
|
1011
|
-
* label: Status[value] // Get the key name for display
|
|
1012
|
-
* }));
|
|
1013
|
-
* ```
|
|
1014
|
-
*
|
|
1015
|
-
* @example
|
|
1016
|
-
* ```typescript
|
|
1017
|
-
* // Mixed numeric enum example
|
|
1018
|
-
* enum HttpStatus {
|
|
1019
|
-
* OK = 200,
|
|
1020
|
-
* NOT_FOUND = 404,
|
|
1021
|
-
* SERVER_ERROR = 500
|
|
1022
|
-
* }
|
|
1023
|
-
*
|
|
1024
|
-
* const httpStatusValues = getEnumValues(HttpStatus);
|
|
1025
|
-
* // Returns: [200, 404, 500]
|
|
1026
|
-
*
|
|
1027
|
-
* // Use for status code validation
|
|
1028
|
-
* function isValidHttpStatus(code: number): code is HttpStatus {
|
|
1029
|
-
* return getEnumValues(HttpStatus).includes(code as HttpStatus);
|
|
1030
|
-
* }
|
|
1031
|
-
* ```
|
|
1032
|
-
*
|
|
1033
|
-
* @example
|
|
1034
|
-
* ```typescript
|
|
1035
|
-
* // Creating dropdown options from enum
|
|
1036
|
-
* enum UserRole {
|
|
1037
|
-
* ADMIN = "admin",
|
|
1038
|
-
* USER = "user",
|
|
1039
|
-
* MODERATOR = "moderator"
|
|
1040
|
-
* }
|
|
1041
|
-
*
|
|
1042
|
-
* const roleOptions = getEnumValues(UserRole).map(role => ({
|
|
1043
|
-
* value: role,
|
|
1044
|
-
* label: role.charAt(0).toUpperCase() + role.slice(1)
|
|
1045
|
-
* }));
|
|
1046
|
-
* // Returns: [
|
|
1047
|
-
* // { value: "admin", label: "Admin" },
|
|
1048
|
-
* // { value: "user", label: "User" },
|
|
1049
|
-
* // { value: "moderator", label: "Moderator" }
|
|
1050
|
-
* // ]
|
|
1051
|
-
* ```
|
|
1052
|
-
*
|
|
1053
|
-
* @remarks
|
|
1054
|
-
* - Automatically detects numeric vs string enums and handles them appropriately
|
|
1055
|
-
* - For numeric enums, filters out reverse mapping entries that TypeScript automatically creates
|
|
1056
|
-
* - For string enums, returns all values as-is since there are no reverse mappings
|
|
1057
|
-
* - Works with mixed numeric enums (enums with explicit numeric values)
|
|
1058
|
-
* - The returned array maintains the order of enum declaration
|
|
1059
|
-
* - Type-safe: the return type is correctly inferred as an array of the enum's value types
|
|
1060
|
-
*
|
|
1061
|
-
* @see {@link Object.values} The underlying method used to extract enum entries
|
|
1062
|
-
*/
|
|
1063
|
-
function getEnumValues(e) {
|
|
1064
|
-
const values = Object.values(e);
|
|
1065
|
-
// In numeric enums, values can appear as keys (reverse mapping)
|
|
1066
|
-
const isNumericEnum = values.some(v => typeof v === "number");
|
|
1067
|
-
return isNumericEnum ? values.filter(v => typeof v !== "string") : values;
|
|
1068
|
-
}
|
|
1069
|
-
//# sourceMappingURL=object.utils.js.map
|