@@ -79,55 +79,43 @@ function diff(obj1, obj2, pathConverter) {
7979 return arr ;
8080 } ) ;
8181
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} ) {
8683 var obj1Keys = Object . keys ( obj1 ) ;
8784 var obj1KeysLength = obj1Keys . length ;
8885 var obj2Keys = Object . keys ( obj2 ) ;
8986 var obj2KeysLength = obj2Keys . length ;
9087 var path ;
9188
92- var newPermutation ;
93-
9489 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- }
10190
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+ }
111101 }
112- }
113102
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
128116 for ( var i = 0 ; i < lengthDelta ; i ++ ) {
129117 path = basePathForRemoves . concat ( i ) ;
130- newPermutation . remove . push ( {
118+ diffs . remove . push ( {
131119 op : 'remove' ,
132120 path : pathConverter ( path ) ,
133121 } ) ;
@@ -144,38 +132,34 @@ function diff(obj1, obj2, pathConverter) {
144132 // since list of removes are reversed before presenting result,
145133 // we need to ignore existing parent removes when doing nested removes
146134 pathForRemoves : basePath . concat ( i + lengthDelta ) ,
147- permutation : newPermutation ,
135+ diffs ,
148136 } ) ;
149137 }
150138 }
151139 }
152140
141+ var diffs = { remove : [ ] , replace : [ ] , add : [ ] } ;
153142 getDiff ( {
154143 obj1,
155144 obj2,
156145 basePath : [ ] ,
157146 basePathForRemoves : [ ] ,
158- permutation : permutations [ 0 ] ,
147+ diffs ,
159148 } ) ;
160149
161- // find the shortest permutation
162- var finalDiffs = permutations . sort (
163- ( a , b ) => diffStepCount ( a ) > diffStepCount ( b ) ? 1 : - 1
164- ) [ 0 ] ;
165-
166150 // reverse removes since we want to maintain indexes
167- return finalDiffs . remove
151+ return diffs . remove
168152 . reverse ( )
169- . concat ( finalDiffs . replace )
170- . concat ( finalDiffs . add ) ;
153+ . concat ( diffs . replace )
154+ . concat ( diffs . add ) ;
171155
172- function pushReplaces ( { key, obj1, obj2, path, pathForRemoves, permutation } ) {
156+ function pushReplaces ( { key, obj1, obj2, path, pathForRemoves, diffs } ) {
173157 var obj1AtKey = obj1 [ key ] ;
174158 var obj2AtKey = obj2 [ key ] ;
175159
176- if ( ! ( key in obj1 ) && obj2AtKey ) {
160+ if ( ! ( key in obj1 ) && ( key in obj2 ) ) {
177161 var obj2Value = obj2AtKey ;
178- permutation . add . push ( {
162+ diffs . add . push ( {
179163 op : 'add' ,
180164 path : pathConverter ( path ) ,
181165 value : obj2Value ,
@@ -184,19 +168,19 @@ function diff(obj1, obj2, pathConverter) {
184168 if ( Object ( obj1AtKey ) !== obj1AtKey ||
185169 Object ( obj2AtKey ) !== obj2AtKey || differentTypes ( obj1AtKey , obj2AtKey )
186170 ) {
187- pushReplace ( path , permutation , obj2AtKey ) ;
171+ pushReplace ( path , diffs , obj2AtKey ) ;
188172 } else {
189173 if ( ! Object . keys ( obj1AtKey ) . length &&
190174 ! Object . keys ( obj2AtKey ) . length &&
191175 String ( obj1AtKey ) != String ( obj2AtKey ) ) {
192- pushReplace ( path , permutation , obj2AtKey ) ;
176+ pushReplace ( path , diffs , obj2AtKey ) ;
193177 } else {
194178 getDiff ( {
195179 obj1 : obj1 [ key ] ,
196180 obj2 : obj2 [ key ] ,
197181 basePath : path ,
198182 basePathForRemoves : pathForRemoves ,
199- permutation } ) ;
183+ diffs } ) ;
200184 }
201185 }
202186 }
@@ -211,22 +195,36 @@ function diff(obj1, obj2, pathConverter) {
211195 }
212196}
213197
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-
226198function jsonPatchPathConverter ( arrayPath ) {
227199 return [ '' ] . concat ( arrayPath ) . join ( '/' ) ;
228200}
229201
230202function differentTypes ( a , b ) {
231203 return Object . prototype . toString . call ( a ) != Object . prototype . toString . call ( b ) ;
232204}
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