@pawells/typescript-common 1.0.0
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/LICENSE +21 -0
- package/README.md +148 -0
- package/build/array/array-chunk.d.ts +10 -0
- package/build/array/array-chunk.d.ts.map +1 -0
- package/build/array/array-chunk.js +19 -0
- package/build/array/array-chunk.js.map +1 -0
- package/build/array/array-compact.d.ts +18 -0
- package/build/array/array-compact.d.ts.map +1 -0
- package/build/array/array-compact.js +22 -0
- package/build/array/array-compact.js.map +1 -0
- package/build/array/array-contains.d.ts +9 -0
- package/build/array/array-contains.d.ts.map +1 -0
- package/build/array/array-contains.js +13 -0
- package/build/array/array-contains.js.map +1 -0
- package/build/array/array-count-by.d.ts +21 -0
- package/build/array/array-count-by.d.ts.map +1 -0
- package/build/array/array-count-by.js +29 -0
- package/build/array/array-count-by.js.map +1 -0
- package/build/array/array-difference.d.ts +25 -0
- package/build/array/array-difference.d.ts.map +1 -0
- package/build/array/array-difference.js +34 -0
- package/build/array/array-difference.js.map +1 -0
- package/build/array/array-element.d.ts +7 -0
- package/build/array/array-element.d.ts.map +1 -0
- package/build/array/array-element.js +2 -0
- package/build/array/array-element.js.map +1 -0
- package/build/array/array-filter.d.ts +37 -0
- package/build/array/array-filter.d.ts.map +1 -0
- package/build/array/array-filter.js +78 -0
- package/build/array/array-filter.js.map +1 -0
- package/build/array/array-flatten.d.ts +16 -0
- package/build/array/array-flatten.d.ts.map +1 -0
- package/build/array/array-flatten.js +20 -0
- package/build/array/array-flatten.js.map +1 -0
- package/build/array/array-group-by.d.ts +13 -0
- package/build/array/array-group-by.d.ts.map +1 -0
- package/build/array/array-group-by.js +24 -0
- package/build/array/array-group-by.js.map +1 -0
- package/build/array/array-intersection.d.ts +25 -0
- package/build/array/array-intersection.d.ts.map +1 -0
- package/build/array/array-intersection.js +39 -0
- package/build/array/array-intersection.js.map +1 -0
- package/build/array/array-partition.d.ts +19 -0
- package/build/array/array-partition.d.ts.map +1 -0
- package/build/array/array-partition.js +32 -0
- package/build/array/array-partition.js.map +1 -0
- package/build/array/array-range.d.ts +18 -0
- package/build/array/array-range.d.ts.map +1 -0
- package/build/array/array-range.js +30 -0
- package/build/array/array-range.js.map +1 -0
- package/build/array/array-sample.d.ts +29 -0
- package/build/array/array-sample.d.ts.map +1 -0
- package/build/array/array-sample.js +19 -0
- package/build/array/array-sample.js.map +1 -0
- package/build/array/array-shuffle.d.ts +8 -0
- package/build/array/array-shuffle.d.ts.map +1 -0
- package/build/array/array-shuffle.js +19 -0
- package/build/array/array-shuffle.js.map +1 -0
- package/build/array/array-sort-by.d.ts +20 -0
- package/build/array/array-sort-by.d.ts.map +1 -0
- package/build/array/array-sort-by.js +31 -0
- package/build/array/array-sort-by.js.map +1 -0
- package/build/array/array-zip.d.ts +22 -0
- package/build/array/array-zip.d.ts.map +1 -0
- package/build/array/array-zip.js +25 -0
- package/build/array/array-zip.js.map +1 -0
- package/build/array/array.test.d.ts +2 -0
- package/build/array/array.test.d.ts.map +1 -0
- package/build/array/array.test.js +347 -0
- package/build/array/array.test.js.map +1 -0
- package/build/array/index.d.ts +27 -0
- package/build/array/index.d.ts.map +1 -0
- package/build/array/index.js +25 -0
- package/build/array/index.js.map +1 -0
- package/build/array/types.d.ts +32 -0
- package/build/array/types.d.ts.map +1 -0
- package/build/array/types.js +2 -0
- package/build/array/types.js.map +1 -0
- package/build/array/unique.d.ts +8 -0
- package/build/array/unique.d.ts.map +1 -0
- package/build/array/unique.js +13 -0
- package/build/array/unique.js.map +1 -0
- package/build/enum/enum-entries.d.ts +11 -0
- package/build/enum/enum-entries.d.ts.map +1 -0
- package/build/enum/enum-entries.js +14 -0
- package/build/enum/enum-entries.js.map +1 -0
- package/build/enum/enum-key-by-value.d.ts +13 -0
- package/build/enum/enum-key-by-value.d.ts.map +1 -0
- package/build/enum/enum-key-by-value.js +21 -0
- package/build/enum/enum-key-by-value.js.map +1 -0
- package/build/enum/enum-keys.d.ts +10 -0
- package/build/enum/enum-keys.d.ts.map +1 -0
- package/build/enum/enum-keys.js +14 -0
- package/build/enum/enum-keys.js.map +1 -0
- package/build/enum/enum-safe-value.d.ts +14 -0
- package/build/enum/enum-safe-value.d.ts.map +1 -0
- package/build/enum/enum-safe-value.js +16 -0
- package/build/enum/enum-safe-value.js.map +1 -0
- package/build/enum/enum-values.d.ts +19 -0
- package/build/enum/enum-values.d.ts.map +1 -0
- package/build/enum/enum-values.js +25 -0
- package/build/enum/enum-values.js.map +1 -0
- package/build/enum/enum.test.d.ts +2 -0
- package/build/enum/enum.test.d.ts.map +1 -0
- package/build/enum/enum.test.js +122 -0
- package/build/enum/enum.test.js.map +1 -0
- package/build/enum/index.d.ts +17 -0
- package/build/enum/index.d.ts.map +1 -0
- package/build/enum/index.js +17 -0
- package/build/enum/index.js.map +1 -0
- package/build/enum/types.d.ts +9 -0
- package/build/enum/types.d.ts.map +1 -0
- package/build/enum/types.js +2 -0
- package/build/enum/types.js.map +1 -0
- package/build/enum/validate-enum-value.d.ts +13 -0
- package/build/enum/validate-enum-value.d.ts.map +1 -0
- package/build/enum/validate-enum-value.js +18 -0
- package/build/enum/validate-enum-value.js.map +1 -0
- package/build/function/compose.d.ts +37 -0
- package/build/function/compose.d.ts.map +1 -0
- package/build/function/compose.js +7 -0
- package/build/function/compose.js.map +1 -0
- package/build/function/debounce.d.ts +25 -0
- package/build/function/debounce.d.ts.map +1 -0
- package/build/function/debounce.js +37 -0
- package/build/function/debounce.js.map +1 -0
- package/build/function/function.test.d.ts +2 -0
- package/build/function/function.test.d.ts.map +1 -0
- package/build/function/function.test.js +158 -0
- package/build/function/function.test.js.map +1 -0
- package/build/function/index.d.ts +17 -0
- package/build/function/index.d.ts.map +1 -0
- package/build/function/index.js +16 -0
- package/build/function/index.js.map +1 -0
- package/build/function/memoize.d.ts +23 -0
- package/build/function/memoize.d.ts.map +1 -0
- package/build/function/memoize.js +34 -0
- package/build/function/memoize.js.map +1 -0
- package/build/function/once.d.ts +17 -0
- package/build/function/once.d.ts.map +1 -0
- package/build/function/once.js +27 -0
- package/build/function/once.js.map +1 -0
- package/build/function/sleep.d.ts +20 -0
- package/build/function/sleep.d.ts.map +1 -0
- package/build/function/sleep.js +22 -0
- package/build/function/sleep.js.map +1 -0
- package/build/function/throttle.d.ts +17 -0
- package/build/function/throttle.d.ts.map +1 -0
- package/build/function/throttle.js +37 -0
- package/build/function/throttle.js.map +1 -0
- package/build/function/types.d.ts +5 -0
- package/build/function/types.d.ts.map +1 -0
- package/build/function/types.js +2 -0
- package/build/function/types.js.map +1 -0
- package/build/index.d.ts +32 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +51 -0
- package/build/index.js.map +1 -0
- package/build/object/assert-object.d.ts +8 -0
- package/build/object/assert-object.d.ts.map +1 -0
- package/build/object/assert-object.js +10 -0
- package/build/object/assert-object.js.map +1 -0
- package/build/object/clone.d.ts +39 -0
- package/build/object/clone.d.ts.map +1 -0
- package/build/object/clone.js +85 -0
- package/build/object/clone.js.map +1 -0
- package/build/object/equals.d.ts +44 -0
- package/build/object/equals.d.ts.map +1 -0
- package/build/object/equals.js +104 -0
- package/build/object/equals.js.map +1 -0
- package/build/object/filter-cached.d.ts +9 -0
- package/build/object/filter-cached.d.ts.map +1 -0
- package/build/object/filter-cached.js +108 -0
- package/build/object/filter-cached.js.map +1 -0
- package/build/object/filter.d.ts +85 -0
- package/build/object/filter.d.ts.map +1 -0
- package/build/object/filter.js +248 -0
- package/build/object/filter.js.map +1 -0
- package/build/object/has-circular-reference.d.ts +8 -0
- package/build/object/has-circular-reference.d.ts.map +1 -0
- package/build/object/has-circular-reference.js +40 -0
- package/build/object/has-circular-reference.js.map +1 -0
- package/build/object/hash.d.ts +20 -0
- package/build/object/hash.d.ts.map +1 -0
- package/build/object/hash.js +39 -0
- package/build/object/hash.js.map +1 -0
- package/build/object/index.d.ts +32 -0
- package/build/object/index.d.ts.map +1 -0
- package/build/object/index.js +30 -0
- package/build/object/index.js.map +1 -0
- package/build/object/key-value-pairs.d.ts +21 -0
- package/build/object/key-value-pairs.d.ts.map +1 -0
- package/build/object/key-value-pairs.js +28 -0
- package/build/object/key-value-pairs.js.map +1 -0
- package/build/object/map-cached.d.ts +9 -0
- package/build/object/map-cached.d.ts.map +1 -0
- package/build/object/map-cached.js +97 -0
- package/build/object/map-cached.js.map +1 -0
- package/build/object/map.d.ts +29 -0
- package/build/object/map.d.ts.map +1 -0
- package/build/object/map.js +40 -0
- package/build/object/map.js.map +1 -0
- package/build/object/merge.d.ts +14 -0
- package/build/object/merge.d.ts.map +1 -0
- package/build/object/merge.js +41 -0
- package/build/object/merge.js.map +1 -0
- package/build/object/object-diff.d.ts +40 -0
- package/build/object/object-diff.d.ts.map +1 -0
- package/build/object/object-diff.js +45 -0
- package/build/object/object-diff.js.map +1 -0
- package/build/object/object-flatten.d.ts +22 -0
- package/build/object/object-flatten.d.ts.map +1 -0
- package/build/object/object-flatten.js +38 -0
- package/build/object/object-flatten.js.map +1 -0
- package/build/object/object-invert.d.ts +20 -0
- package/build/object/object-invert.d.ts.map +1 -0
- package/build/object/object-invert.js +26 -0
- package/build/object/object-invert.js.map +1 -0
- package/build/object/object.test.d.ts +2 -0
- package/build/object/object.test.d.ts.map +1 -0
- package/build/object/object.test.js +432 -0
- package/build/object/object.test.js.map +1 -0
- package/build/object/omit.d.ts +8 -0
- package/build/object/omit.d.ts.map +1 -0
- package/build/object/omit.js +17 -0
- package/build/object/omit.js.map +1 -0
- package/build/object/pick.d.ts +50 -0
- package/build/object/pick.d.ts.map +1 -0
- package/build/object/pick.js +60 -0
- package/build/object/pick.js.map +1 -0
- package/build/object/property-paths.d.ts +115 -0
- package/build/object/property-paths.d.ts.map +1 -0
- package/build/object/property-paths.js +170 -0
- package/build/object/property-paths.js.map +1 -0
- package/build/object/security-utils.d.ts +59 -0
- package/build/object/security-utils.d.ts.map +1 -0
- package/build/object/security-utils.js +165 -0
- package/build/object/security-utils.js.map +1 -0
- package/build/object/sort-keys.d.ts +26 -0
- package/build/object/sort-keys.d.ts.map +1 -0
- package/build/object/sort-keys.js +52 -0
- package/build/object/sort-keys.js.map +1 -0
- package/build/object/types.d.ts +152 -0
- package/build/object/types.d.ts.map +1 -0
- package/build/object/types.js +6 -0
- package/build/object/types.js.map +1 -0
- package/build/string/case-conversion.d.ts +50 -0
- package/build/string/case-conversion.d.ts.map +1 -0
- package/build/string/case-conversion.js +94 -0
- package/build/string/case-conversion.js.map +1 -0
- package/build/string/formatting.d.ts +108 -0
- package/build/string/formatting.d.ts.map +1 -0
- package/build/string/formatting.js +171 -0
- package/build/string/formatting.js.map +1 -0
- package/build/string/index.d.ts +15 -0
- package/build/string/index.d.ts.map +1 -0
- package/build/string/index.js +14 -0
- package/build/string/index.js.map +1 -0
- package/build/string/string.test.d.ts +2 -0
- package/build/string/string.test.d.ts.map +1 -0
- package/build/string/string.test.js +321 -0
- package/build/string/string.test.js.map +1 -0
- package/build/string/transformation.d.ts +18 -0
- package/build/string/transformation.d.ts.map +1 -0
- package/build/string/transformation.js +32 -0
- package/build/string/transformation.js.map +1 -0
- package/build/string/types.d.ts +44 -0
- package/build/string/types.d.ts.map +1 -0
- package/build/string/types.js +2 -0
- package/build/string/types.js.map +1 -0
- package/build/string/validation.d.ts +18 -0
- package/build/string/validation.d.ts.map +1 -0
- package/build/string/validation.js +26 -0
- package/build/string/validation.js.map +1 -0
- package/build/time/elapsed-time/constants.d.ts +10 -0
- package/build/time/elapsed-time/constants.d.ts.map +1 -0
- package/build/time/elapsed-time/constants.js +96 -0
- package/build/time/elapsed-time/constants.js.map +1 -0
- package/build/time/elapsed-time/elapsed-time.d.ts +412 -0
- package/build/time/elapsed-time/elapsed-time.d.ts.map +1 -0
- package/build/time/elapsed-time/elapsed-time.js +652 -0
- package/build/time/elapsed-time/elapsed-time.js.map +1 -0
- package/build/time/elapsed-time/types.d.ts +150 -0
- package/build/time/elapsed-time/types.d.ts.map +1 -0
- package/build/time/elapsed-time/types.js +2 -0
- package/build/time/elapsed-time/types.js.map +1 -0
- package/build/time/elapsed-time/utils.d.ts +18 -0
- package/build/time/elapsed-time/utils.d.ts.map +1 -0
- package/build/time/elapsed-time/utils.js +24 -0
- package/build/time/elapsed-time/utils.js.map +1 -0
- package/build/time/index.d.ts +18 -0
- package/build/time/index.d.ts.map +1 -0
- package/build/time/index.js +17 -0
- package/build/time/index.js.map +1 -0
- package/build/time/stopwatch/entry-types.d.ts +13 -0
- package/build/time/stopwatch/entry-types.d.ts.map +1 -0
- package/build/time/stopwatch/entry-types.js +2 -0
- package/build/time/stopwatch/entry-types.js.map +1 -0
- package/build/time/stopwatch/entry.d.ts +80 -0
- package/build/time/stopwatch/entry.d.ts.map +1 -0
- package/build/time/stopwatch/entry.js +105 -0
- package/build/time/stopwatch/entry.js.map +1 -0
- package/build/time/stopwatch/stopwatch.d.ts +232 -0
- package/build/time/stopwatch/stopwatch.d.ts.map +1 -0
- package/build/time/stopwatch/stopwatch.js +315 -0
- package/build/time/stopwatch/stopwatch.js.map +1 -0
- package/build/time/time.test.d.ts +2 -0
- package/build/time/time.test.d.ts.map +1 -0
- package/build/time/time.test.js +211 -0
- package/build/time/time.test.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performs a deep comparison between two values to determine if they are equivalent.
|
|
3
|
+
* Handles primitives, objects, arrays, dates, regular expressions, and special values like NaN.
|
|
4
|
+
*
|
|
5
|
+
* @param a - First value to compare
|
|
6
|
+
* @param b - Second value to compare
|
|
7
|
+
* @returns True if the values are equivalent, false otherwise
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // Primitive values
|
|
12
|
+
* ObjectEquals(42, 42); // true
|
|
13
|
+
* ObjectEquals('hello', 'hello'); // true
|
|
14
|
+
* ObjectEquals(true, false); // false
|
|
15
|
+
*
|
|
16
|
+
* // Objects
|
|
17
|
+
* ObjectEquals({ a: 1, b: 2 }, { a: 1, b: 2 }); // true
|
|
18
|
+
* ObjectEquals({ a: 1, b: 2 }, { a: 1, b: 3 }); // false
|
|
19
|
+
*
|
|
20
|
+
* // Nested objects
|
|
21
|
+
* const obj1 = { user: { name: 'John', age: 30 }, active: true };
|
|
22
|
+
* const obj2 = { user: { name: 'John', age: 30 }, active: true };
|
|
23
|
+
* ObjectEquals(obj1, obj2); // true
|
|
24
|
+
*
|
|
25
|
+
* // Arrays
|
|
26
|
+
* ObjectEquals([1, 2, 3], [1, 2, 3]); // true
|
|
27
|
+
* ObjectEquals([1, [2, 3]], [1, [2, 3]]); // true
|
|
28
|
+
*
|
|
29
|
+
* // Date objects
|
|
30
|
+
* const date1 = new Date('2023-01-01');
|
|
31
|
+
* const date2 = new Date('2023-01-01');
|
|
32
|
+
* ObjectEquals(date1, date2); // true
|
|
33
|
+
*
|
|
34
|
+
* // Regular expressions
|
|
35
|
+
* ObjectEquals(/abc/g, /abc/g); // true
|
|
36
|
+
* ObjectEquals(/abc/g, /abc/i); // false
|
|
37
|
+
*
|
|
38
|
+
* // Special cases
|
|
39
|
+
* ObjectEquals(NaN, NaN); // true (unlike === comparison)
|
|
40
|
+
* ObjectEquals(null, undefined); // false
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function ObjectEquals(a, b) {
|
|
44
|
+
// If the values are strictly equal, return true
|
|
45
|
+
if (a === b)
|
|
46
|
+
return true;
|
|
47
|
+
// Handle NaN special case - NaN should equal NaN for assertion purposes
|
|
48
|
+
if (typeof a === 'number' && typeof b === 'number' && isNaN(a) && isNaN(b)) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
// If either value is null, undefined, or not an object, they can't be equivalent
|
|
52
|
+
if (a === null || b === null || a === undefined || b === undefined)
|
|
53
|
+
return false;
|
|
54
|
+
if (typeof a !== typeof b)
|
|
55
|
+
return false;
|
|
56
|
+
if (typeof a !== 'object')
|
|
57
|
+
return false;
|
|
58
|
+
// Handle Date objects
|
|
59
|
+
if (a instanceof Date && b instanceof Date) {
|
|
60
|
+
return a.getTime() === b.getTime();
|
|
61
|
+
}
|
|
62
|
+
// Handle RegExp objects
|
|
63
|
+
if (a instanceof RegExp && b instanceof RegExp) {
|
|
64
|
+
return a.toString() === b.toString();
|
|
65
|
+
}
|
|
66
|
+
// Handle arrays
|
|
67
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
68
|
+
if (a.length !== b.length)
|
|
69
|
+
return false;
|
|
70
|
+
// Compare each element in the arrays
|
|
71
|
+
for (let i = 0; i < a.length; i++) {
|
|
72
|
+
if (!ObjectEquals(a[i], b[i]))
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
// If one is array and the other isn't, they're not equal
|
|
78
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
79
|
+
return false;
|
|
80
|
+
// Compare object properties
|
|
81
|
+
const keysA = Object.keys(a);
|
|
82
|
+
const keysB = Object.keys(b);
|
|
83
|
+
if (keysA.length !== keysB.length)
|
|
84
|
+
return false;
|
|
85
|
+
// Also compare symbol properties
|
|
86
|
+
const symbolsA = Object.getOwnPropertySymbols(a);
|
|
87
|
+
const symbolsB = Object.getOwnPropertySymbols(b);
|
|
88
|
+
if (symbolsA.length !== symbolsB.length)
|
|
89
|
+
return false;
|
|
90
|
+
for (const key of keysA) {
|
|
91
|
+
if (!Object.hasOwn(b, key))
|
|
92
|
+
return false;
|
|
93
|
+
if (!ObjectEquals(a[key], b[key]))
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
for (const symbol of symbolsA) {
|
|
97
|
+
if (!Object.getOwnPropertySymbols(b).includes(symbol))
|
|
98
|
+
return false;
|
|
99
|
+
if (!ObjectEquals(a[symbol], b[symbol]))
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=equals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"equals.js","sourceRoot":"","sources":["../../src/object/equals.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ICachedObjectFilterOptions, TCachedObjectFilterFunction } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a cached version of filter for improved performance when filtering the same objects repeatedly
|
|
4
|
+
*
|
|
5
|
+
* @param options Configuration options for both the cache and the underlying filter
|
|
6
|
+
* @returns A cached version of the filter function
|
|
7
|
+
*/
|
|
8
|
+
export declare function ObjectFilterCached<T extends object>(options?: ICachedObjectFilterOptions): TCachedObjectFilterFunction<T>;
|
|
9
|
+
//# sourceMappingURL=filter-cached.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-cached.d.ts","sourceRoot":"","sources":["../../src/object/filter-cached.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,0BAA0B,EAAE,2BAA2B,EAAwB,MAAM,YAAY,CAAC;AAOhH;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,2BAA2B,CAAC,CAAC,CAAC,CAmH7H"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { ObjectFilter } from './filter.js';
|
|
2
|
+
import { ObjectHash } from './hash.js';
|
|
3
|
+
// Cache configuration constants
|
|
4
|
+
const DEFAULT_MAX_CACHE_SIZE = 1000;
|
|
5
|
+
const CACHE_EVICTION_PERCENTAGE = 0.2; // 20%
|
|
6
|
+
const INITIAL_CACHE_HASH_LENGTH = 16;
|
|
7
|
+
/**
|
|
8
|
+
* Creates a cached version of filter for improved performance when filtering the same objects repeatedly
|
|
9
|
+
*
|
|
10
|
+
* @param options Configuration options for both the cache and the underlying filter
|
|
11
|
+
* @returns A cached version of the filter function
|
|
12
|
+
*/
|
|
13
|
+
export function ObjectFilterCached(options = {}) {
|
|
14
|
+
const { maxCacheSize = DEFAULT_MAX_CACHE_SIZE, useDeepEqual = false, caseInsensitiveStrings = false, validatePaths = true, } = options;
|
|
15
|
+
const cache = new Map();
|
|
16
|
+
let totalCachedEntries = 0;
|
|
17
|
+
/**
|
|
18
|
+
* Evicts approximately 20% of entries from the largest per-filter cache bucket
|
|
19
|
+
* when the total number of cached results exceeds `maxCacheSize`.
|
|
20
|
+
*
|
|
21
|
+
* The eviction strategy targets the most-populated bucket to reclaim the most
|
|
22
|
+
* memory in a single pass while preserving results for other active filters.
|
|
23
|
+
*/
|
|
24
|
+
const clearOldestEntries = () => {
|
|
25
|
+
if (cache.size === 0)
|
|
26
|
+
return;
|
|
27
|
+
// Find the cache with the most entries
|
|
28
|
+
let largestCache = null;
|
|
29
|
+
let largestCacheKey = '';
|
|
30
|
+
let largestSize = 0;
|
|
31
|
+
for (const [key, objectCache] of cache.entries()) {
|
|
32
|
+
if (objectCache.size > largestSize) {
|
|
33
|
+
largestCache = objectCache;
|
|
34
|
+
largestCacheKey = key;
|
|
35
|
+
largestSize = objectCache.size;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (largestCache) {
|
|
39
|
+
// Remove approximately 20% of entries from the largest cache
|
|
40
|
+
const entriesToRemove = Math.ceil(largestSize * CACHE_EVICTION_PERCENTAGE);
|
|
41
|
+
let removed = 0;
|
|
42
|
+
for (const key of largestCache.keys()) {
|
|
43
|
+
largestCache.delete(key);
|
|
44
|
+
removed++;
|
|
45
|
+
if (removed >= entriesToRemove)
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
// If the cache is now empty, remove it completely
|
|
49
|
+
if (largestCache.size === 0) {
|
|
50
|
+
cache.delete(largestCacheKey);
|
|
51
|
+
}
|
|
52
|
+
totalCachedEntries -= removed;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
return (cursor, filter) => {
|
|
56
|
+
// Validate inputs
|
|
57
|
+
if (!cursor || typeof cursor !== 'object') {
|
|
58
|
+
return Promise.resolve(false);
|
|
59
|
+
}
|
|
60
|
+
if (!filter || typeof filter !== 'object') {
|
|
61
|
+
return Promise.resolve(true); // Empty filter matches everything
|
|
62
|
+
}
|
|
63
|
+
// Optimize cache key generation with shorter hash for better performance
|
|
64
|
+
const filterKey = ObjectHash(filter).substring(0, INITIAL_CACHE_HASH_LENGTH);
|
|
65
|
+
// Try to get the object cache for this filter
|
|
66
|
+
let objectCache = cache.get(filterKey);
|
|
67
|
+
if (!objectCache) {
|
|
68
|
+
objectCache = new Map();
|
|
69
|
+
cache.set(filterKey, objectCache);
|
|
70
|
+
}
|
|
71
|
+
// Create optimized hash-based cache key for the object being filtered
|
|
72
|
+
// Use shorter hash for better memory efficiency and performance
|
|
73
|
+
let objectKey;
|
|
74
|
+
try {
|
|
75
|
+
objectKey = ObjectHash(cursor).substring(0, INITIAL_CACHE_HASH_LENGTH);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// If object has circular references, skip caching and compute directly
|
|
79
|
+
const filterOptions = {
|
|
80
|
+
useDeepEqual,
|
|
81
|
+
caseInsensitiveStrings,
|
|
82
|
+
validatePaths,
|
|
83
|
+
};
|
|
84
|
+
return Promise.resolve(ObjectFilter(cursor, filter, filterOptions));
|
|
85
|
+
}
|
|
86
|
+
// Check if we have a cached result
|
|
87
|
+
if (objectCache.has(objectKey)) {
|
|
88
|
+
const cachedResult = objectCache.get(objectKey);
|
|
89
|
+
return Promise.resolve(cachedResult ?? false);
|
|
90
|
+
}
|
|
91
|
+
// Calculate the result
|
|
92
|
+
const filterOptions = {
|
|
93
|
+
useDeepEqual,
|
|
94
|
+
caseInsensitiveStrings,
|
|
95
|
+
validatePaths,
|
|
96
|
+
};
|
|
97
|
+
const result = ObjectFilter(cursor, filter, filterOptions);
|
|
98
|
+
// Cache the result
|
|
99
|
+
objectCache.set(objectKey, result);
|
|
100
|
+
totalCachedEntries++;
|
|
101
|
+
// Ensure the cache size does not exceed the limit
|
|
102
|
+
if (totalCachedEntries > maxCacheSize) {
|
|
103
|
+
clearOldestEntries();
|
|
104
|
+
}
|
|
105
|
+
return Promise.resolve(result);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=filter-cached.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-cached.js","sourceRoot":"","sources":["../../src/object/filter-cached.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { IObjectFilterOptions, TPropertyFilter } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Filters an object based on a provided filter using flexible matching criteria.
|
|
4
|
+
* Supports dot notation for nested properties, deep equality comparison, case-insensitive string matching,
|
|
5
|
+
* and array inclusion/subset matching.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of object being filtered
|
|
8
|
+
* @param object - The object to filter
|
|
9
|
+
* @param filter - The filter criteria to apply
|
|
10
|
+
* @param options - Optional configuration for filtering behavior
|
|
11
|
+
* @returns True if the object satisfies the filter, false otherwise
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // Basic property matching
|
|
16
|
+
* const user = { name: 'John', age: 30, active: true };
|
|
17
|
+
* ObjectFilter(user, { name: 'John' }); // true
|
|
18
|
+
* ObjectFilter(user, { age: 25 }); // false
|
|
19
|
+
*
|
|
20
|
+
* // Multiple properties (AND logic)
|
|
21
|
+
* ObjectFilter(user, { name: 'John', active: true }); // true
|
|
22
|
+
* ObjectFilter(user, { name: 'John', age: 25 }); // false
|
|
23
|
+
*
|
|
24
|
+
* // Dot notation for nested properties
|
|
25
|
+
* const profile = {
|
|
26
|
+
* user: { name: 'John', details: { age: 30, city: 'NYC' } },
|
|
27
|
+
* meta: { created: '2023-01-01', active: true }
|
|
28
|
+
* };
|
|
29
|
+
* ObjectFilter(profile, { 'user.name': 'John' }); // true
|
|
30
|
+
* ObjectFilter(profile, { 'user.details.city': 'NYC' }); // true
|
|
31
|
+
* ObjectFilter(profile, { 'meta.active': true }); // true
|
|
32
|
+
*
|
|
33
|
+
* // Case-insensitive string matching
|
|
34
|
+
* ObjectFilter(user, { name: 'john' }, { caseInsensitiveStrings: true }); // true
|
|
35
|
+
* ObjectFilter(user, { name: 'JOHN' }, { caseInsensitiveStrings: true }); // true
|
|
36
|
+
*
|
|
37
|
+
* // Array matching - inclusion check
|
|
38
|
+
* const post = { tags: ['javascript', 'typescript', 'node'], category: 'tech' };
|
|
39
|
+
* ObjectFilter(post, { tags: 'javascript' }); // true (string in array)
|
|
40
|
+
* ObjectFilter(post, { tags: 'python' }); // false
|
|
41
|
+
*
|
|
42
|
+
* // Array subset matching
|
|
43
|
+
* ObjectFilter(post, { tags: ['javascript', 'typescript'] }); // true (subset)
|
|
44
|
+
* ObjectFilter(post, { tags: ['javascript', 'python'] }); // false (not subset)
|
|
45
|
+
*
|
|
46
|
+
* // Deep equality for nested objects
|
|
47
|
+
* const order = {
|
|
48
|
+
* items: [{ id: 1, name: 'Book' }, { id: 2, name: 'Pen' }],
|
|
49
|
+
* shipping: { address: { city: 'NYC', zip: '10001' } }
|
|
50
|
+
* };
|
|
51
|
+
* ObjectFilter(order,
|
|
52
|
+
* { shipping: { address: { city: 'NYC' } } },
|
|
53
|
+
* { useDeepEqual: true }
|
|
54
|
+
* ); // true (partial match with deep equality)
|
|
55
|
+
*
|
|
56
|
+
* // Path validation (security)
|
|
57
|
+
* ObjectFilter(profile, { '../malicious': 'value' }, { validatePaths: true }); // false
|
|
58
|
+
* ObjectFilter(profile, { 'user..name': 'John' }, { validatePaths: true }); // false
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function ObjectFilter<T>(object: T, filter: Record<string, any>, options?: IObjectFilterOptions): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Filters an object's properties using a predicate function.
|
|
64
|
+
* Creates a new object containing only the properties for which the predicate returns true.
|
|
65
|
+
*
|
|
66
|
+
* @template T - The type of the source object
|
|
67
|
+
* @param obj - The object to filter
|
|
68
|
+
* @param predicate - A function that takes a property key and value, returning true to include the property
|
|
69
|
+
* @returns A new object with only the filtered properties
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const obj = { a: 1, b: 2, c: 3, d: 4 };
|
|
74
|
+
* const filtered = FilterObject(obj, (key, value) => value > 2);
|
|
75
|
+
* // Result: { c: 3, d: 4 }
|
|
76
|
+
*
|
|
77
|
+
* // Type-safe filtering
|
|
78
|
+
* interface User { id: number; name: string; age: number; }
|
|
79
|
+
* const user: User = { id: 1, name: 'John', age: 30 };
|
|
80
|
+
* const publicData = FilterObject(user, (key, value) => key !== 'age');
|
|
81
|
+
* // Result: { id: 1, name: 'John' }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function FilterObject<T extends object>(obj: T, predicate: TPropertyFilter<T>): Partial<T>;
|
|
85
|
+
//# sourceMappingURL=filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/object/filter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA2HxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAgDnH;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAiBhG"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { ObjectEquals } from './equals.js';
|
|
2
|
+
import { isPropertyPathSafe } from './security-utils.js';
|
|
3
|
+
/**
|
|
4
|
+
* Navigates to a nested property value using dot notation path
|
|
5
|
+
*
|
|
6
|
+
* @param object - The object to navigate
|
|
7
|
+
* @param path - The dot notation path (e.g., 'meta.details.category')
|
|
8
|
+
* @returns The value at the path, or undefined if path doesn't exist
|
|
9
|
+
*/
|
|
10
|
+
function objectGetValueByPath(object, path) {
|
|
11
|
+
const pathSegments = path.split('.');
|
|
12
|
+
let current = object;
|
|
13
|
+
for (const segment of pathSegments) {
|
|
14
|
+
current = current?.[segment];
|
|
15
|
+
if (current === undefined || current === null) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return current;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Compares two objects using deep equality logic for ObjectFilter
|
|
23
|
+
*
|
|
24
|
+
* @param objValue - The object value to compare
|
|
25
|
+
* @param filterValue - The filter value to compare against
|
|
26
|
+
* @param caseInsensitiveStrings - Whether to use case-insensitive string comparison
|
|
27
|
+
* @returns True if the values match according to deep equality rules
|
|
28
|
+
*/
|
|
29
|
+
function objectDeepComparison(objValue, filterValue, caseInsensitiveStrings = false) {
|
|
30
|
+
// Handle string comparison with case insensitivity option
|
|
31
|
+
if (caseInsensitiveStrings && typeof objValue === 'string' && typeof filterValue === 'string') {
|
|
32
|
+
return objValue.toLowerCase() === filterValue.toLowerCase();
|
|
33
|
+
}
|
|
34
|
+
// Special handling for Date objects
|
|
35
|
+
if (objValue instanceof Date && filterValue instanceof Date) {
|
|
36
|
+
return objValue.getTime() === filterValue.getTime();
|
|
37
|
+
}
|
|
38
|
+
// Handle array cases: if objValue is an array, use array comparison logic
|
|
39
|
+
if (Array.isArray(objValue)) {
|
|
40
|
+
return objectHandleArrayComparison(objValue, filterValue, caseInsensitiveStrings, true);
|
|
41
|
+
}
|
|
42
|
+
// For deeply nested structures, check if filter is a subset of the object
|
|
43
|
+
if (typeof objValue === 'object' && objValue !== null && typeof filterValue === 'object' && filterValue !== null && !Array.isArray(filterValue)) {
|
|
44
|
+
return ObjectFilter(objValue, filterValue, { useDeepEqual: true, caseInsensitiveStrings });
|
|
45
|
+
}
|
|
46
|
+
// Default case: use full object equality
|
|
47
|
+
return ObjectEquals(objValue, filterValue);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Handles array filtering logic with support for string inclusion and subset matching
|
|
51
|
+
*
|
|
52
|
+
* @param objValue - The array value from the object
|
|
53
|
+
* @param filterValue - The filter value to match against
|
|
54
|
+
* @param caseInsensitiveStrings - Whether to use case-insensitive string comparison
|
|
55
|
+
* @param useDeepEqual - Whether to use deep equality for nested objects
|
|
56
|
+
* @returns True if the filter matches the array value
|
|
57
|
+
*/
|
|
58
|
+
function objectHandleArrayComparison(objValue, filterValue, caseInsensitiveStrings, useDeepEqual) {
|
|
59
|
+
// If filterValue is a string, check if it exists in the array
|
|
60
|
+
if (typeof filterValue === 'string') {
|
|
61
|
+
if (caseInsensitiveStrings) {
|
|
62
|
+
return objValue.some((item) => typeof item === 'string' && item.toLowerCase() === filterValue.toLowerCase());
|
|
63
|
+
}
|
|
64
|
+
return objValue.includes(filterValue);
|
|
65
|
+
}
|
|
66
|
+
// If filterValue is also an array, check if it's a subset
|
|
67
|
+
if (Array.isArray(filterValue)) {
|
|
68
|
+
return filterValue.every((item) => objValue.some((objItem) => objectCompareValues(objItem, item, caseInsensitiveStrings, useDeepEqual)));
|
|
69
|
+
}
|
|
70
|
+
// For non-string, non-array filter values, check direct inclusion
|
|
71
|
+
return objValue.some((item) => objectCompareValues(item, filterValue, caseInsensitiveStrings, useDeepEqual));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Compare values based on filter criteria
|
|
75
|
+
*
|
|
76
|
+
* @param objValue The object value
|
|
77
|
+
* @param filterValue The filter value to compare against
|
|
78
|
+
* @param caseInsensitiveStrings Whether to do case-insensitive string comparison
|
|
79
|
+
* @param useDeepEqual Whether to use deep equality for nested objects
|
|
80
|
+
* @returns True if values match according to specified criteria
|
|
81
|
+
*/
|
|
82
|
+
function objectCompareValues(objValue, filterValue, caseInsensitiveStrings, useDeepEqual = false) {
|
|
83
|
+
// Check if objValue is an array
|
|
84
|
+
if (Array.isArray(objValue)) {
|
|
85
|
+
return objectHandleArrayComparison(objValue, filterValue, caseInsensitiveStrings, useDeepEqual);
|
|
86
|
+
}
|
|
87
|
+
// Handle string comparison with case insensitivity option
|
|
88
|
+
if (caseInsensitiveStrings && typeof objValue === 'string' && typeof filterValue === 'string') {
|
|
89
|
+
return objValue.toLowerCase() === filterValue.toLowerCase();
|
|
90
|
+
}
|
|
91
|
+
// Special handling for NaN
|
|
92
|
+
if (typeof objValue === 'number' && typeof filterValue === 'number' && isNaN(objValue) && isNaN(filterValue)) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
// For object filtering with useDeepEqual, check if filterValue is a subset of objValue
|
|
96
|
+
if (useDeepEqual && typeof objValue === 'object' && objValue !== null && typeof filterValue === 'object' && filterValue !== null && !Array.isArray(filterValue)) {
|
|
97
|
+
return ObjectFilter(objValue, filterValue, { useDeepEqual: true, caseInsensitiveStrings });
|
|
98
|
+
}
|
|
99
|
+
// Use deep equality if requested
|
|
100
|
+
if (useDeepEqual) {
|
|
101
|
+
return ObjectEquals(objValue, filterValue);
|
|
102
|
+
}
|
|
103
|
+
// Default strict equality
|
|
104
|
+
return objValue === filterValue;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Filters an object based on a provided filter using flexible matching criteria.
|
|
108
|
+
* Supports dot notation for nested properties, deep equality comparison, case-insensitive string matching,
|
|
109
|
+
* and array inclusion/subset matching.
|
|
110
|
+
*
|
|
111
|
+
* @template T - The type of object being filtered
|
|
112
|
+
* @param object - The object to filter
|
|
113
|
+
* @param filter - The filter criteria to apply
|
|
114
|
+
* @param options - Optional configuration for filtering behavior
|
|
115
|
+
* @returns True if the object satisfies the filter, false otherwise
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* // Basic property matching
|
|
120
|
+
* const user = { name: 'John', age: 30, active: true };
|
|
121
|
+
* ObjectFilter(user, { name: 'John' }); // true
|
|
122
|
+
* ObjectFilter(user, { age: 25 }); // false
|
|
123
|
+
*
|
|
124
|
+
* // Multiple properties (AND logic)
|
|
125
|
+
* ObjectFilter(user, { name: 'John', active: true }); // true
|
|
126
|
+
* ObjectFilter(user, { name: 'John', age: 25 }); // false
|
|
127
|
+
*
|
|
128
|
+
* // Dot notation for nested properties
|
|
129
|
+
* const profile = {
|
|
130
|
+
* user: { name: 'John', details: { age: 30, city: 'NYC' } },
|
|
131
|
+
* meta: { created: '2023-01-01', active: true }
|
|
132
|
+
* };
|
|
133
|
+
* ObjectFilter(profile, { 'user.name': 'John' }); // true
|
|
134
|
+
* ObjectFilter(profile, { 'user.details.city': 'NYC' }); // true
|
|
135
|
+
* ObjectFilter(profile, { 'meta.active': true }); // true
|
|
136
|
+
*
|
|
137
|
+
* // Case-insensitive string matching
|
|
138
|
+
* ObjectFilter(user, { name: 'john' }, { caseInsensitiveStrings: true }); // true
|
|
139
|
+
* ObjectFilter(user, { name: 'JOHN' }, { caseInsensitiveStrings: true }); // true
|
|
140
|
+
*
|
|
141
|
+
* // Array matching - inclusion check
|
|
142
|
+
* const post = { tags: ['javascript', 'typescript', 'node'], category: 'tech' };
|
|
143
|
+
* ObjectFilter(post, { tags: 'javascript' }); // true (string in array)
|
|
144
|
+
* ObjectFilter(post, { tags: 'python' }); // false
|
|
145
|
+
*
|
|
146
|
+
* // Array subset matching
|
|
147
|
+
* ObjectFilter(post, { tags: ['javascript', 'typescript'] }); // true (subset)
|
|
148
|
+
* ObjectFilter(post, { tags: ['javascript', 'python'] }); // false (not subset)
|
|
149
|
+
*
|
|
150
|
+
* // Deep equality for nested objects
|
|
151
|
+
* const order = {
|
|
152
|
+
* items: [{ id: 1, name: 'Book' }, { id: 2, name: 'Pen' }],
|
|
153
|
+
* shipping: { address: { city: 'NYC', zip: '10001' } }
|
|
154
|
+
* };
|
|
155
|
+
* ObjectFilter(order,
|
|
156
|
+
* { shipping: { address: { city: 'NYC' } } },
|
|
157
|
+
* { useDeepEqual: true }
|
|
158
|
+
* ); // true (partial match with deep equality)
|
|
159
|
+
*
|
|
160
|
+
* // Path validation (security)
|
|
161
|
+
* ObjectFilter(profile, { '../malicious': 'value' }, { validatePaths: true }); // false
|
|
162
|
+
* ObjectFilter(profile, { 'user..name': 'John' }, { validatePaths: true }); // false
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
export function ObjectFilter(object, filter, options = {}) {
|
|
166
|
+
// Default options
|
|
167
|
+
const { useDeepEqual = false, caseInsensitiveStrings = false, validatePaths = false } = options;
|
|
168
|
+
for (const key in filter) {
|
|
169
|
+
// Skip properties that are not own properties of the filter object
|
|
170
|
+
if (!Object.hasOwn(filter, key))
|
|
171
|
+
continue;
|
|
172
|
+
// Handle dot notation paths
|
|
173
|
+
if (key.includes('.')) {
|
|
174
|
+
if (validatePaths && !isPropertyPathSafe(key)) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
const objValue = objectGetValueByPath(object, key);
|
|
178
|
+
if (objValue === undefined) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
// Compare the values
|
|
182
|
+
const filterValue = filter[key];
|
|
183
|
+
if (useDeepEqual) {
|
|
184
|
+
if (!objectDeepComparison(objValue, filterValue, caseInsensitiveStrings)) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else if (!objectCompareValues(objValue, filterValue, caseInsensitiveStrings, useDeepEqual)) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// Direct property comparison
|
|
194
|
+
const objValue = object[key];
|
|
195
|
+
const filterValue = filter[key];
|
|
196
|
+
if (!(key in object)) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
if (useDeepEqual) {
|
|
200
|
+
if (!objectDeepComparison(objValue, filterValue, caseInsensitiveStrings)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else if (!objectCompareValues(objValue, filterValue, caseInsensitiveStrings, useDeepEqual)) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Filters an object's properties using a predicate function.
|
|
213
|
+
* Creates a new object containing only the properties for which the predicate returns true.
|
|
214
|
+
*
|
|
215
|
+
* @template T - The type of the source object
|
|
216
|
+
* @param obj - The object to filter
|
|
217
|
+
* @param predicate - A function that takes a property key and value, returning true to include the property
|
|
218
|
+
* @returns A new object with only the filtered properties
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* const obj = { a: 1, b: 2, c: 3, d: 4 };
|
|
223
|
+
* const filtered = FilterObject(obj, (key, value) => value > 2);
|
|
224
|
+
* // Result: { c: 3, d: 4 }
|
|
225
|
+
*
|
|
226
|
+
* // Type-safe filtering
|
|
227
|
+
* interface User { id: number; name: string; age: number; }
|
|
228
|
+
* const user: User = { id: 1, name: 'John', age: 30 };
|
|
229
|
+
* const publicData = FilterObject(user, (key, value) => key !== 'age');
|
|
230
|
+
* // Result: { id: 1, name: 'John' }
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
export function FilterObject(obj, predicate) {
|
|
234
|
+
if (!obj || typeof obj !== 'object') {
|
|
235
|
+
return {};
|
|
236
|
+
}
|
|
237
|
+
const result = {};
|
|
238
|
+
for (const key in obj) {
|
|
239
|
+
if (Object.hasOwn(obj, key)) {
|
|
240
|
+
const value = obj[key];
|
|
241
|
+
if (predicate(key, value)) {
|
|
242
|
+
result[key] = value;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/object/filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether an object contains circular references
|
|
3
|
+
*
|
|
4
|
+
* @param obj - The object to check for circular references
|
|
5
|
+
* @returns True if the object contains circular references, false otherwise
|
|
6
|
+
*/
|
|
7
|
+
export declare function ObjectHasCircularReference(obj: unknown): boolean;
|
|
8
|
+
//# sourceMappingURL=has-circular-reference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has-circular-reference.d.ts","sourceRoot":"","sources":["../../src/object/has-circular-reference.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAyChE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether an object contains circular references
|
|
3
|
+
*
|
|
4
|
+
* @param obj - The object to check for circular references
|
|
5
|
+
* @returns True if the object contains circular references, false otherwise
|
|
6
|
+
*/
|
|
7
|
+
export function ObjectHasCircularReference(obj) {
|
|
8
|
+
const seen = new WeakSet();
|
|
9
|
+
const detectCircular = (value) => {
|
|
10
|
+
// Only check for circular references in non-null objects
|
|
11
|
+
if (value === null || typeof value !== 'object') {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
// If we've seen this object before, it's a circular reference
|
|
15
|
+
if (seen.has(value)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
// Mark this object as seen
|
|
19
|
+
seen.add(value);
|
|
20
|
+
// For arrays, check each element
|
|
21
|
+
if (Array.isArray(value)) {
|
|
22
|
+
for (const item of value) {
|
|
23
|
+
if (detectCircular(item)) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
// For objects, check each own enumerable property
|
|
30
|
+
const keys = Object.keys(value);
|
|
31
|
+
for (const key of keys) {
|
|
32
|
+
if (detectCircular(value[key])) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
38
|
+
return detectCircular(obj);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=has-circular-reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has-circular-reference.js","sourceRoot":"","sources":["../../src/object/has-circular-reference.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,GAAY;IACtD,MAAM,IAAI,GAAG,IAAI,OAAO,EAAE,CAAC;IAE3B,MAAM,cAAc,GAAG,CAAC,KAAc,EAAW,EAAE;QAClD,yDAAyD;QACzD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACjD,OAAO,KAAK,CAAC;QACd,CAAC;QAED,8DAA8D;QAC9D,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,2BAA2B;QAC3B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEhB,iCAAiC;QACjC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBAC1B,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC1B,OAAO,IAAI,CAAC;gBACb,CAAC;YACF,CAAC;YAED,OAAO,KAAK,CAAC;QACd,CAAC;QAED,kDAAkD;QAClD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC;QAE3D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,cAAc,CAAE,KAAiC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC7D,OAAO,IAAI,CAAC;YACb,CAAC;QACF,CAAC;QAED,OAAO,KAAK,CAAC;IACd,CAAC,CAAC;IAEF,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC"}
|