@@ -4,43 +4,104 @@ const isObject = (value: any) =>
4
4
Object . prototype . toString . call ( value ) === "[object Object]" ;
5
5
6
6
function findPaginatedResourcePath ( responseData : any ) : string [ ] {
7
- const paginatedResourcePath = deepFindPathToProperty (
7
+ const paginatedResourcePath : string [ ] | null = deepFindPathToProperty (
8
8
responseData ,
9
9
"pageInfo" ,
10
10
) ;
11
- if ( paginatedResourcePath . length === 0 ) {
11
+ if ( paginatedResourcePath === null ) {
12
12
throw new MissingPageInfo ( responseData ) ;
13
13
}
14
14
return paginatedResourcePath ;
15
15
}
16
16
17
- const deepFindPathToProperty = (
18
- object : any ,
19
- searchProp : string ,
20
- path : string [ ] = [ ] ,
21
- ) : string [ ] => {
22
- for ( const key of Object . keys ( object ) ) {
23
- const currentPath = [ ...path , key ] ;
24
- const currentValue = object [ key ] ;
25
-
26
- if ( currentValue . hasOwnProperty ( searchProp ) ) {
27
- return currentPath ;
28
- }
17
+ type TreeNode = [ key : string , value : any , depth : number ] ;
29
18
30
- if ( isObject ( currentValue ) ) {
31
- const result = deepFindPathToProperty (
32
- currentValue ,
33
- searchProp ,
34
- currentPath ,
35
- ) ;
36
- if ( result . length > 0 ) {
37
- return result ;
19
+ function getDirectPropertyPath ( preOrderTraversalPropertyPath : TreeNode [ ] ) {
20
+ const terminalNodeDepth : number =
21
+ preOrderTraversalPropertyPath [ preOrderTraversalPropertyPath . length - 1 ] [ 2 ] ;
22
+
23
+ const alreadyConsideredDepth : { [ key : string ] : boolean } = { } ;
24
+ const directPropertyPath : TreeNode [ ] = preOrderTraversalPropertyPath
25
+ . reverse ( )
26
+ . filter ( ( node : TreeNode ) => {
27
+ const nodeDepth : number = node [ 2 ] ;
28
+
29
+ if ( nodeDepth >= terminalNodeDepth || alreadyConsideredDepth [ nodeDepth ] ) {
30
+ return false ;
38
31
}
32
+
33
+ alreadyConsideredDepth [ nodeDepth ] = true ;
34
+ return true ;
35
+ } )
36
+ . reverse ( ) ;
37
+
38
+ return directPropertyPath ;
39
+ }
40
+
41
+ function makeTreeNodeChildrenFromData (
42
+ data : any ,
43
+ depth : number ,
44
+ searchProperty : string ,
45
+ ) : TreeNode [ ] {
46
+ return isObject ( data )
47
+ ? Object . keys ( data )
48
+ . reverse ( )
49
+ . sort ( ( a , b ) => {
50
+ if ( searchProperty === a ) {
51
+ return 1 ;
52
+ }
53
+
54
+ if ( searchProperty === b ) {
55
+ return - 1 ;
56
+ }
57
+
58
+ return 0 ;
59
+ } )
60
+ . map ( ( key ) => [ key , data [ key ] , depth ] )
61
+ : [ ] ;
62
+ }
63
+
64
+ function findPathToObjectContainingProperty (
65
+ data : any ,
66
+ searchProperty : string ,
67
+ ) : string [ ] | null {
68
+ const preOrderTraversalPropertyPath : TreeNode [ ] = [ ] ;
69
+ const stack : TreeNode [ ] = makeTreeNodeChildrenFromData (
70
+ data ,
71
+ 1 ,
72
+ searchProperty ,
73
+ ) ;
74
+
75
+ while ( stack . length > 0 ) {
76
+ const node : TreeNode = stack . pop ( ) ! ;
77
+
78
+ preOrderTraversalPropertyPath . push ( node ) ;
79
+
80
+ if ( searchProperty === node [ 0 ] ) {
81
+ const directPropertyPath : TreeNode [ ] = getDirectPropertyPath (
82
+ preOrderTraversalPropertyPath ,
83
+ ) ;
84
+ return directPropertyPath . map ( ( node : TreeNode ) => node [ 0 ] ) ;
39
85
}
86
+
87
+ const depth : number = node [ 2 ] + 1 ;
88
+ const edges : TreeNode [ ] = makeTreeNodeChildrenFromData (
89
+ node [ 1 ] ,
90
+ depth ,
91
+ searchProperty ,
92
+ ) ;
93
+ stack . push ( ...edges ) ;
40
94
}
41
95
42
- return [ ] ;
43
- } ;
96
+ return null ;
97
+ }
98
+
99
+ function deepFindPathToProperty (
100
+ object : any ,
101
+ searchProp : string ,
102
+ ) : string [ ] | null {
103
+ return findPathToObjectContainingProperty ( object , searchProp ) ;
104
+ }
44
105
45
106
/**
46
107
* The interfaces of the "get" and "set" functions are equal to those of lodash:
0 commit comments