You can delete properties of an object while you're running a for loop over
that object. No special tricks are required.

If you delete a property that's already been iterated (including the current
iteration), nothing special happens - the iteration continues on its merry
way. If you delete a property that hasn't yet been iterated, it won't be.

Just for fun, here's a primitive little cache implementation that lets you
add/remove/get items and also prune items from the beginning of the list:

    function Cache() {
        var itemsByKey = {};
        var keysByIndex = [];
        
        this.add = function( key, value ) {
            var item = itemsByKey[key];
            if( item ) {
                item.value = value;
            }
            else {
                itemsByKey[key] = { value: value, index: keysByIndex.length
};
                keysByIndex.push( key );
            }
        };
        
        this.remove = function( key ) {
            var item = itemsByKey[key];
            if( item ) {
                keysByIndex.splice( item.index, 1 );
                delete itemsByKey[key];
            }
        };
        
        this.get = function( key ) {
            var item = itemsByKey[key];
            return item && item.value;
        };
        
        this.prune = function( n ) {
            n = Math.min( n, keysByIndex.length );
            for( var i = 0;  i < n;  ++i ) {
                delete itemsByKey[ keysByIndex[i] ];
            }
            keysByIndex.splice( 0, n );
        };
        
        // for testing
        this.dump = function() {
            console.debug( keysByIndex, itemsByKey );
        };
    }

    var c = new Cache;
    c.add('a','A'); c.add('b','B'); c.add('c','C'); c.add('d','D');
    c.dump();
    console.debug( c.get('b') );
    c.add('b','BB');  // duplicate key
    console.debug( c.get('b') );
    c.dump();
    c.remove('b');
    console.debug( c.get('b') );
    c.dump();
    c.prune(2);
    c.dump();

Pasted into the Firebug multiline console, this should dump:

  ["a", "b", "c", "d"] Object a=Object b=Object c=Object d=Object
  B
  BB
  ["a", "b", "c", "d"] Object a=Object b=Object c=Object d=Object
  undefined
  ["a", "c", "d"] Object a=Object c=Object d=Object
  ["d"] Object d=Object

-Mike

> From: Pops
> 
> What I wanted to use this for was my cache and to "truncate" 
> old data.  I see that using a real array will allow me to use 
> the inherited .length property to set a new size and it will 
> do truncation.  But since I am using an associated array, the 
> length property is no longer usable.
> 
> So if I use the for loop with delete, will I be pulling the 
> "rug from under its feet?"
> 
> Typically, in code designs like this, you would do a reverse 
> traversal to delete the last entries first so you can keep 
> with the internal loop counters and/or references.
> 
> In other words, is this "safe?"
> 
>   function pruneCache(amt) {
>     var n = 0;
>     for (var i in cache) {
>         if (amt > 0) {
>            amt--;
>            delete cache[i];
>            return;
>         }
>         n++;
>     }
>     cache.length = n;
>   }
> 
> If not, then in lieu of a reverse loop syntax,  I would probably need
> to copy the cache first?   I have not checked but if returning false
> stops the traveral that would be more efficient.
> 
> I could recode all this into a pure indexed array and then 
> use the .length, but then I would need a fast lookup method 
> or 2nd matrix to map the associated name with the array index.

Reply via email to