Javascript instant iterators, refined

Friday, 22 Jul 2005 [Tuesday, 7 Feb 2006]

Reading David Flanagan’s Another Useful JavaScript Closure had me revisit a short snippet of Javascript to create simple iterators I wrote about a while ago. In his piece, David debates what should happen when the iterator code he shows (in turn gleaned from Bob Ippolito) reaches the end of its list: whether it should throw an exception as shown, or rather return a sentinel value, as David would prefer. For what it’s worth, my opinion is that reaching the end of an iterator is just not an exceptional condition.

The iterator code Bob wrote is one where you only get to retrieve the (new) current element when you advance the iterator. In the code I wrote, you can retrieve the current element at any time you wish, as well as when advancing the iterator. It occurred to me that this style of iterator can solve the ambiguity of the return value cleanly and unintrusively. In so doing, I can also remove the conceptual redundancy of having two ways of retrieving the current element. The trick is simply to return true from .next() while there’s something to iterate over. Thus, the code now looks like so:

function iterate( iterable ) {
  var i = -1;
  var iterator = function() { return i < 0 ? null : i < iterable.length ? iterable[ i ] : null; };
  iterator.next = function() { return ++i < iterable.length };
  return iterator;
}

This works as is if dropped into the example code in my old post:

for( var sheet = iterate( document.styleSheets ); sheet.next(); )
  if( sheet().href )
    do_something( sheet() );

That reminds me:

Carter’s Compass: I know I’m on the right track when by deleting code I’m adding functionality.

Actually, this style of iterator is much better written like so:

function iterate( iterable ) {
  var i = -1;
  var getter = function() { return i < 0 ? null : i < iterable.length ? iterable[ i ] : null; };
  return function() { return ++i < iterable.length ? getter : null };
}

It can then be used thusly:

for( var sheet, next = iterate( document.styleSheets ); sheet = next(); )
  if( sheet().href )
    do_something( sheet() );