44 * MIT license
55 */
66import { _deepClone , _objectKeys , escapePathComponent , hasOwnProperty } from './helpers.js' ;
7+ import lodashDifference from './lodash-difference.js'
78import { applyPatch , Operation } from './core.js' ;
89
910export interface Observer < T > {
@@ -139,6 +140,81 @@ export function generate<T>(observer: Observer<Object>, invertible = false): Ope
139140 return temp ;
140141}
141142
143+ /*
144+ * 'compareArrays' doesnt do a good job with ordering,
145+ * if bad ordering was detected, or bad job, return false, and revert
146+ * to old code. its most likely that there isnt much added value anyway
147+ *
148+ */
149+
150+ function testOrder ( arr1 , arr2 , patches ) {
151+ // we dont want to mess up with arr1
152+ // the patches are just remove / add so need to clone deep
153+ let clonedArr1 = arr1 . map ( e => e ) ;
154+
155+ applyPatch ( clonedArr1 , patches ) ;
156+ if ( clonedArr1 . length !== arr2 . length ) {
157+ return false ;
158+ }
159+ for ( let index = 0 ; index < arr2 . length ; index ++ ) {
160+ if ( clonedArr1 [ index ] !== arr2 [ index ] ) {
161+ return false ;
162+ }
163+ }
164+ return true ;
165+
166+ }
167+
168+ /*
169+ * return array efficient array patches when possible.
170+ * in frequenct cases of arrays additions or removals, where an element was removed, or added.
171+ * and thats the only difference between the arrays, and all other elements are the exact same (===)
172+ * then the code bellow can do a great job and having a very small number of patches.
173+ * in some cases it will revert back to the old behaviour.
174+ *
175+ */
176+
177+ function compareArrays ( arr1 , arr2 , path , invertible ) {
178+ let diff = lodashDifference ( arr1 , arr2 ) ;
179+ if ( diff . length === arr1 . length ) {
180+ // this means that the the arrays are completly different
181+ // and there is no added value in this function - revert to old behaviour
182+ return [ ] ;
183+ }
184+ let removePatches = [ ] ;
185+ diff . forEach ( value => {
186+ const index = arr1 . indexOf ( value ) ;
187+
188+ const op = 'remove' ;
189+ removePatches . push ( {
190+ op,
191+ path : "/" + index
192+ } )
193+ if ( invertible ) {
194+ removePatches . push ( {
195+ op : 'test' ,
196+ path : "/" + index ,
197+ value
198+ } ) ;
199+ }
200+ } ) ;
201+ diff = lodashDifference ( arr2 , arr1 ) ;
202+ const addPatches = diff . map ( value => {
203+ const index = arr2 . indexOf ( value ) ;
204+ const op = 'add' ;
205+ return {
206+ op,
207+ value,
208+ path : "/" + index
209+ }
210+ } ) ;
211+ const finalPatches = removePatches . reverse ( ) . concat ( addPatches ) ;
212+ if ( testOrder ( arr1 , arr2 , finalPatches ) ) {
213+ return finalPatches . map ( p => ( { ...p , path : path + p . path } ) ) ;
214+ }
215+ return [ ]
216+ }
217+
142218// Dirty check if obj is different from mirror, generate patches and update mirror
143219function _generate ( mirror , obj , patches , path , invertible ) {
144220 if ( obj === mirror ) {
@@ -154,6 +230,13 @@ function _generate(mirror, obj, patches, path, invertible) {
154230 var changed = false ;
155231 var deleted = false ;
156232
233+ if ( Array . isArray ( mirror ) && Array . isArray ( obj ) ) {
234+ const newPatches = compareArrays ( mirror , obj , path , invertible )
235+ if ( newPatches . length ) {
236+ return newPatches . forEach ( p => patches . push ( p ) ) ;
237+ }
238+ }
239+
157240 //if ever "move" operation is implemented here, make sure this test runs OK: "should not generate the same patch twice (move)"
158241
159242 for ( var t = oldKeys . length - 1 ; t >= 0 ; t -- ) {
0 commit comments