@@ -79,55 +79,43 @@ function diff(obj1, obj2, pathConverter) {
79
79
return arr ;
80
80
} ) ;
81
81
82
- // we will gather all permutations and return the one with the fewest diffs
83
- var permutations = [ { remove : [ ] , replace : [ ] , add : [ ] } ] ;
84
-
85
- function getDiff ( { obj1, obj2, basePath, basePathForRemoves, permutation} ) {
82
+ function getDiff ( { obj1, obj2, basePath, basePathForRemoves, diffs} ) {
86
83
var obj1Keys = Object . keys ( obj1 ) ;
87
84
var obj1KeysLength = obj1Keys . length ;
88
85
var obj2Keys = Object . keys ( obj2 ) ;
89
86
var obj2KeysLength = obj2Keys . length ;
90
87
var path ;
91
88
92
- var newPermutation ;
93
-
94
89
var lengthDelta = obj1 . length - obj2 . length ;
95
- // if both objects are arrays and obj1 length > obj2 length
96
- // we create an additional permutation that trims obj1 from left
97
- if ( Array . isArray ( obj1 ) && Array . isArray ( obj2 ) && lengthDelta > 0 ) {
98
- newPermutation = clonePermutation ( permutation ) ;
99
- permutations . push ( newPermutation ) ;
100
- }
101
90
102
- // trim from right
103
- for ( var i = 0 ; i < obj1KeysLength ; i ++ ) {
104
- var key = Array . isArray ( obj1 ) ? Number ( obj1Keys [ i ] ) : obj1Keys [ i ] ;
105
- if ( ! ( key in obj2 ) ) {
106
- path = basePathForRemoves . concat ( key ) ;
107
- permutation . remove . push ( {
108
- op : 'remove' ,
109
- path : pathConverter ( path ) ,
110
- } ) ;
91
+ if ( trimFromRight ( obj1 , obj2 ) ) {
92
+ for ( var i = 0 ; i < obj1KeysLength ; i ++ ) {
93
+ var key = Array . isArray ( obj1 ) ? Number ( obj1Keys [ i ] ) : obj1Keys [ i ] ;
94
+ if ( ! ( key in obj2 ) ) {
95
+ path = basePathForRemoves . concat ( key ) ;
96
+ diffs . remove . push ( {
97
+ op : 'remove' ,
98
+ path : pathConverter ( path ) ,
99
+ } ) ;
100
+ }
111
101
}
112
- }
113
102
114
- for ( var i = 0 ; i < obj2KeysLength ; i ++ ) {
115
- var key = Array . isArray ( obj2 ) ? Number ( obj2Keys [ i ] ) : obj2Keys [ i ] ;
116
- pushReplaces ( {
117
- key,
118
- obj1,
119
- obj2,
120
- path : basePath . concat ( key ) ,
121
- pathForRemoves : basePath . concat ( key ) ,
122
- permutation,
123
- } ) ;
124
- }
125
-
126
- // if we created a new permutation above it means we should also try trimming from left
127
- if ( newPermutation ) {
103
+ for ( var i = 0 ; i < obj2KeysLength ; i ++ ) {
104
+ var key = Array . isArray ( obj2 ) ? Number ( obj2Keys [ i ] ) : obj2Keys [ i ] ;
105
+ pushReplaces ( {
106
+ key,
107
+ obj1,
108
+ obj2,
109
+ path : basePath . concat ( key ) ,
110
+ pathForRemoves : basePath . concat ( key ) ,
111
+ diffs,
112
+ } ) ;
113
+ }
114
+ } else {
115
+ // trim from left, objects are both arrays
128
116
for ( var i = 0 ; i < lengthDelta ; i ++ ) {
129
117
path = basePathForRemoves . concat ( i ) ;
130
- newPermutation . remove . push ( {
118
+ diffs . remove . push ( {
131
119
op : 'remove' ,
132
120
path : pathConverter ( path ) ,
133
121
} ) ;
@@ -144,38 +132,34 @@ function diff(obj1, obj2, pathConverter) {
144
132
// since list of removes are reversed before presenting result,
145
133
// we need to ignore existing parent removes when doing nested removes
146
134
pathForRemoves : basePath . concat ( i + lengthDelta ) ,
147
- permutation : newPermutation ,
135
+ diffs ,
148
136
} ) ;
149
137
}
150
138
}
151
139
}
152
140
141
+ var diffs = { remove : [ ] , replace : [ ] , add : [ ] } ;
153
142
getDiff ( {
154
143
obj1,
155
144
obj2,
156
145
basePath : [ ] ,
157
146
basePathForRemoves : [ ] ,
158
- permutation : permutations [ 0 ] ,
147
+ diffs ,
159
148
} ) ;
160
149
161
- // find the shortest permutation
162
- var finalDiffs = permutations . sort (
163
- ( a , b ) => diffStepCount ( a ) > diffStepCount ( b ) ? 1 : - 1
164
- ) [ 0 ] ;
165
-
166
150
// reverse removes since we want to maintain indexes
167
- return finalDiffs . remove
151
+ return diffs . remove
168
152
. reverse ( )
169
- . concat ( finalDiffs . replace )
170
- . concat ( finalDiffs . add ) ;
153
+ . concat ( diffs . replace )
154
+ . concat ( diffs . add ) ;
171
155
172
- function pushReplaces ( { key, obj1, obj2, path, pathForRemoves, permutation } ) {
156
+ function pushReplaces ( { key, obj1, obj2, path, pathForRemoves, diffs } ) {
173
157
var obj1AtKey = obj1 [ key ] ;
174
158
var obj2AtKey = obj2 [ key ] ;
175
159
176
- if ( ! ( key in obj1 ) && obj2AtKey ) {
160
+ if ( ! ( key in obj1 ) && ( key in obj2 ) ) {
177
161
var obj2Value = obj2AtKey ;
178
- permutation . add . push ( {
162
+ diffs . add . push ( {
179
163
op : 'add' ,
180
164
path : pathConverter ( path ) ,
181
165
value : obj2Value ,
@@ -184,19 +168,19 @@ function diff(obj1, obj2, pathConverter) {
184
168
if ( Object ( obj1AtKey ) !== obj1AtKey ||
185
169
Object ( obj2AtKey ) !== obj2AtKey || differentTypes ( obj1AtKey , obj2AtKey )
186
170
) {
187
- pushReplace ( path , permutation , obj2AtKey ) ;
171
+ pushReplace ( path , diffs , obj2AtKey ) ;
188
172
} else {
189
173
if ( ! Object . keys ( obj1AtKey ) . length &&
190
174
! Object . keys ( obj2AtKey ) . length &&
191
175
String ( obj1AtKey ) != String ( obj2AtKey ) ) {
192
- pushReplace ( path , permutation , obj2AtKey ) ;
176
+ pushReplace ( path , diffs , obj2AtKey ) ;
193
177
} else {
194
178
getDiff ( {
195
179
obj1 : obj1 [ key ] ,
196
180
obj2 : obj2 [ key ] ,
197
181
basePath : path ,
198
182
basePathForRemoves : pathForRemoves ,
199
- permutation } ) ;
183
+ diffs } ) ;
200
184
}
201
185
}
202
186
}
@@ -211,22 +195,36 @@ function diff(obj1, obj2, pathConverter) {
211
195
}
212
196
}
213
197
214
- function clonePermutation ( permutation ) {
215
- return {
216
- remove : permutation . remove . slice ( 0 ) ,
217
- replace : permutation . replace . slice ( 0 ) ,
218
- add : permutation . add . slice ( 0 ) ,
219
- } ;
220
- }
221
-
222
- function diffStepCount ( permutation ) {
223
- return permutation . remove . length + permutation . replace . length + permutation . add . length ;
224
- }
225
-
226
198
function jsonPatchPathConverter ( arrayPath ) {
227
199
return [ '' ] . concat ( arrayPath ) . join ( '/' ) ;
228
200
}
229
201
230
202
function differentTypes ( a , b ) {
231
203
return Object . prototype . toString . call ( a ) != Object . prototype . toString . call ( b ) ;
232
204
}
205
+
206
+ function trimFromRight ( obj1 , obj2 ) {
207
+ var lengthDelta = obj1 . length - obj2 . length ;
208
+ if ( Array . isArray ( obj1 ) && Array . isArray ( obj2 ) && lengthDelta > 0 ) {
209
+ var leftMatches = 0 ;
210
+ var rightMatches = 0 ;
211
+ for ( var i = 0 ; i < obj2 . length ; i ++ ) {
212
+ if ( String ( obj1 [ i ] ) === String ( obj2 [ i ] ) ) {
213
+ leftMatches ++ ;
214
+ } else {
215
+ break ;
216
+ }
217
+ }
218
+ for ( var j = obj2 . length ; j > 0 ; j -- ) {
219
+ if ( String ( obj1 [ j + lengthDelta ] ) === String ( obj2 [ j ] ) ) {
220
+ rightMatches ++ ;
221
+ } else {
222
+ break ;
223
+ }
224
+ }
225
+
226
+ // bias to trim right becase it requires less index shifting
227
+ return leftMatches >= rightMatches ;
228
+ }
229
+ return true ;
230
+ }
0 commit comments