-
Notifications
You must be signed in to change notification settings - Fork 0
/
depth_finder.ts
134 lines (125 loc) · 4.5 KB
/
depth_finder.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Types that can be handled successfully by the getDepth() function.
// Add a new set of expectations to helpers.test.ts for additional types
// before adding the new types here.
type FathomableObject =
string | number | boolean | null | {} |
(string | number | boolean | null | object)[] |
{[key: string]: (string | number | boolean | null | {[key: string]: (string | number | boolean | null)})} |
{[key: number]: (string | number | boolean | null | {[key: number]: (string | number | boolean | null)})}
/**
* Gets the depth of an Array (maximum level of nesting for all elements)
*/
export const getArrayDepth = (testObject: unknown): number => {
return Array.isArray(testObject) ?
1 + Math.max(...testObject.map(getArrayDepth)) :
0;
}
/**
* Gets the depth of an object
* Arrays or key:value objects have depth equal to their max nesting level
* Other types (number, string, etc.) have depth 0
*/
export const getDepth = (testObject: FathomableObject): number => {
// console.log(`#### getDepth called on`, testObject);
if (isKeyValueObject(testObject)) {
testObject = objectValuesToArray(testObject)
}
return Array.isArray(testObject) ?
1 + Math.max(...testObject.map(getDepth)) :
0;
}
export const isKeyValueObject = (testObject: unknown) => {
// console.log(`%%% isKeyValueObject called on`, testObject);
return (
testObject !== null &&
typeof testObject === 'object' &&
Array.isArray(testObject) === false &&
//@ts-ignore
!!Object.keys(testObject).length
)
}
/**
* Recursive function to deep compare the values in two elements
* The function considers objects with matching keys and values to be equivalent
* Handles these types:
*
* Boolean
* Number
* BigInt
* String
* Symbol
* Date
* Key-value object (POJO)
* Array
*/
export const compare = (item1: any, item2: any) => {
let comparisonResult = true;
if (isDate(item1) && isDate(item2)) {
comparisonResult = Object.is(item1, item2)
} else if (typeof item1 !== typeof item2) {
comparisonResult = false
} else if ( Array.isArray(item1) && Array.isArray(item2) ) {
if (item1.length !== item2.length) {
comparisonResult = false;
} else {
for (let i = 0; i < item1.length; i++) {
if (!compare(item1[i], item2[i])) {
comparisonResult = false;
break
}
}
}
} else if (typeof item1 === 'object' && typeof item2 === 'object') {
if (Object.keys(item1).length !== Object.keys(item2).length) {
comparisonResult = false;
} else {
// get an array of [key,value] arrays
const key_values_1 = Object.entries(item1);
const key_values_2 = Object.entries(item2);
for (let i = 0; i < key_values_1.length; i++) {
if (
// compare keys
!compare(key_values_1[i][0], key_values_2[i][0]) ||
// compare values
!compare(key_values_1[i][1], key_values_2[i][1])
) {
comparisonResult = false
break
}
}
}
} else if (typeof item1 === 'symbol' && typeof item2 === 'symbol') {
// NOTE: symbols are unique, even if they are constructed using an identical seed, e.g.
// let symbol1 = Symbol('one');
// let symbol2 = Symbol('one');
// symbol1 == symbol1 // true
// symbol1 == symbol2 // false
// symbol1 === symbol1 // true
// symbol1 === symbol2 // false
return item1 === item2
} else {
console.log(`Object.is(${item1}, ${item2}) = ${Object.is(item1, item2)}`);
comparisonResult = Object.is(item1, item2)
}
return comparisonResult
}
export const isDate = (item: any): boolean => {
return Object.prototype.toString.call(item) === "[object Date]"
}
// private
/**
* Recursive function to convert an object to an array
* Object values are used as array elements, keys are discarded
*/
const objectValuesToArray = (theObject: FathomableObject) => {
// console.log(`$$$$ objectValuesToArray called on`, theObject);
if (isKeyValueObject(theObject)) {
let returnArray: any[] = [];
Object.values(theObject as object).forEach(value => {
returnArray.push(objectValuesToArray(value))
});
return returnArray
} else {
return theObject
}
}