6363 this . _promise = readyPromise ;
6464 this . _destroyFn = destroyFn ;
6565
66+ // indexCache is a weak hashmap (a lazy list) of keys to array indices,
67+ // items are not guaranteed to stay up to date in this list (since the list
68+ // can be manually edited without calling the $ methods) and it should
69+ // always be used with skepticism regarding whether it is accurate
70+ // (see $indexFor() below for proper usage)
71+ this . _indexCache = { } ;
72+
6673 // Array.isArray will not work on objects which extend the Array class.
6774 // So instead of extending the Array class, we just return an actual array.
6875 // However, it's still possible to extend FirebaseArray and have the public methods
175182 */
176183 $indexFor : function ( key ) {
177184 var self = this ;
178- // todo optimize and/or cache these? they wouldn't need to be perfect
179- return this . $list . findIndex ( function ( rec ) { return self . _getKey ( rec ) === key ; } ) ;
185+ var cache = self . _indexCache ;
186+ // evaluate whether our key is cached and, if so, whether it is up to date
187+ if ( ! cache . hasOwnProperty ( key ) || self . $keyAt ( cache [ key ] ) !== key ) {
188+ // update the hashmap
189+ var pos = self . $list . findIndex ( function ( rec ) { return self . _getKey ( rec ) === key ; } ) ;
190+ if ( pos !== - 1 ) {
191+ cache [ key ] = pos ;
192+ }
193+ }
194+ return cache . hasOwnProperty ( key ) ? cache [ key ] : - 1 ;
180195 } ,
181196
182197 /**
349364 * @returns {string||null }
350365 * @private
351366 */
352- _getKey : function ( rec ) {
367+ _getKey : function ( rec ) { //todo rename this to $$getId
353368 return angular . isObject ( rec ) ? rec . $id : null ;
354369 } ,
355370
366381 $$process : function ( event , rec , prevChild ) {
367382 var key = this . _getKey ( rec ) ;
368383 var changed = false ;
369- var pos ;
384+ var curPos ;
370385 switch ( event ) {
371386 case 'child_added' :
372- pos = this . $indexFor ( key ) ;
387+ curPos = this . $indexFor ( key ) ;
373388 break ;
374389 case 'child_moved' :
375- pos = this . $indexFor ( key ) ;
390+ curPos = this . $indexFor ( key ) ;
376391 this . _spliceOut ( key ) ;
377392 break ;
378393 case 'child_removed' :
385400 default :
386401 throw new Error ( 'Invalid event type ' + event ) ;
387402 }
388- if ( angular . isDefined ( pos ) ) {
403+ if ( angular . isDefined ( curPos ) ) {
389404 // add it to the array
390- changed = this . _addAfter ( rec , prevChild ) !== pos ;
405+ changed = this . _addAfter ( rec , prevChild ) !== curPos ;
391406 }
392407 if ( changed ) {
393408 // send notifications to anybody monitoring $watch
433448 if ( i === 0 ) { i = this . $list . length ; }
434449 }
435450 this . $list . splice ( i , 0 , rec ) ;
451+ this . _indexCache [ this . _getKey ( rec ) ] = i ;
436452 return i ;
437453 } ,
438454
447463 _spliceOut : function ( key ) {
448464 var i = this . $indexFor ( key ) ;
449465 if ( i > - 1 ) {
466+ delete this . _indexCache [ key ] ;
450467 return this . $list . splice ( i , 1 ) [ 0 ] ;
451468 }
452469 return null ;
466483 return list [ indexOrItem ] ;
467484 }
468485 else if ( angular . isObject ( indexOrItem ) ) {
469- var i = list . length ;
470- while ( i -- ) {
471- if ( list [ i ] === indexOrItem ) {
472- return indexOrItem ;
473- }
474- }
486+ // it must be an item in this array; it's not sufficient for it just to have
487+ // a $id or even a $id that is in the array, it must be an actual record
488+ // the fastest way to determine this is to use $getRecord (to avoid iterating all recs)
489+ // and compare the two
490+ var key = this . _getKey ( indexOrItem ) ;
491+ var rec = this . $getRecord ( key ) ;
492+ return rec === indexOrItem ? rec : null ;
475493 }
476494 return null ;
477495 } ,
530548 return FirebaseArray ;
531549 }
532550 ] ) ;
533- } ) ( ) ;
551+ } ) ( ) ;
0 commit comments