Avoid filter infinite digest without caching JSON string.

88 views
Skip to first unread message

Johnny Hauser

unread,
Apr 25, 2014, 11:10:56 PM4/25/14
to ang...@googlegroups.com
I've got this filter for breaking up an array into smaller arrays based on the "size" argument. Here's a jsbin of the non-working version: http://jsbin.com/UmOMAgA/80/edit
Check out some previous revisions (a lot of people have toyed around with it) for the working version using a cache.

app.filter('partition', function() {
  return function(arr, size) {
    var newArr = [];
    
    for (var i=0; i<arr.length; i+=size) {
        newArr.push(arr.slice(i, i+size));        
    }
    
    return newArr;
  }; 
});

This causes an infinite digest. I think that's because of the new nested arrays. The only solution I've found is to do some awkward caching with JSON strings:

app.filter('partition', function($cacheFactory) {
  var arrayCache = $cacheFactory('partition');
  var filter = function(arr, size) {
    if (!arr) { return; }
    var newArr = [];
    for (var i=0; i<arr.length; i+=size) {
        newArr.push(arr.slice(i, i+size));        
    }
    var cachedParts;
    var arrString = JSON.stringify(arr);
    cachedParts = arrayCache.get(arrString+size); 
    if (JSON.stringify(cachedParts) === JSON.stringify(newArr)) {
      return cachedParts;
    }
    arrayCache.put(arrString+size, newArr);
    return newArr;
  };
  return filter;
});

There's got to be a better way, right!?

Sander Elias

unread,
Apr 26, 2014, 2:38:27 AM4/26/14
to ang...@googlegroups.com

Hi Johnny,

The only way to do this without caching is prepare the data in the controller, before rendering it.
The reason it keeps on digesting is because every time you return a new set of array’s, those are not known to angular, so angular goes forth and tags it with an $id, which, in turn causes an digest wich.. (restart reading this sentence!)

Ok, now you have broken out of the infinitive reading loop of above ;) I will show you a more elegant, yet unusable option. This is one of the thing that ES6 shines at.
this only works in FF or in chrome if you have set the “Enable Experimental JavaScript” flag.

app.filter('partition', function() {
  var mp  = new Map();  

  var filter = function(arr, size,killCache) {
    if (killCache) {mp.clear();}
    if (!arr) { return; }
    if (mp.has(size) && mp.get(size).has(arr)) {
      return mp.get(size).get(arr);
    }    

    
var newArr = [];

    for (var i=0
; i<arr.length; i+=size) {
        newArr.push(arr.slice(i, i+size));        
    }

    if (!mp.has(size)) {
      // new size item, create it!
      mp.set(size,new WeakMap());
    }
    mp.get(size).set(arr,newArr);      

    
return newArr;
  };

  return filter;  
});

See it in action: http://jsbin.com/UmOMAgA/88/edit

Regards
Sander

Message has been deleted

Johnny Hauser

unread,
Aug 14, 2014, 2:24:12 PM8/14/14
to ang...@googlegroups.com
I created a service that abstracts the "caching" that stabilizes the filter. I really prefer it over transforming data in the controller. I'm now using it for my partition filter and for a Fisher-Yates shuffle filter.

Reply all
Reply to author
Forward
0 new messages