Do you know a JavaScript library that implements a generic Iterator class for collections (be it Arrays or some abstract Enumerable) with a full set of features, like the Google Common or the Apache Commons?
Edit: Enumerable#each
is not an Iterator class. I'm looking for an Iterator, something that would let us write something like:
var iterator = new Iterator(myCollection);
for (var element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
// iterator
}
Edit : mamoo reminded us of the Iterator implementation in Mozilla's Javascript 1.7. So the goal now is to find an implementation of this Iterator function in Javascript 1.5 (ECMA 4).
Edit2 : Why using an iterator when libraries (and ECMA 5) provide a each
method? First, because each
usually messes with this
because the callback is call
-ed (that's why each
accepts a second argument in Prototype). Then, because people are much more familiar with the for(;;)
construct than with the .each(callback)
construct (at least, in my field). Lastly, because an iterator can iterate over plain objects (see JavaScript 1.7).
Edit3 : I accepted npup's anwser, but here is my shot at it :
function Iterator(o, keysOnly) {
if (!(this instanceof arguments.callee))
return new arguments.callee(o, keysOnly);
var index = 0, keys = [];
if (!o || typeof o != "object") return;
if ('splice' in o && 'join' in o) {
while(keys.length < o.length) keys.push(keys.length);
} else {
for (p in o) if (o.hasOwnProperty(p)) keys.push(p);
}
this.next = function next() {
if (index < keys.length) {
var key = keys[index++];
return keysOnly ? key : [key, o[key]];
} else throw { name: "StopIteration" };
};
this.hasNext = function hasNext() {
return index < keys.length;
};
}
var lang = { name: 'JavaScript', birthYear: 1995 };
var it = Iterator(lang);
while (it.hasNext()) {
alert(it.next());
}
//alert(it.next()); // A StopIteration exception is thrown
var langs = ['JavaScript', 'Python', 'C++'];
var it = Iterator(langs);
while (it.hasNext()) {
alert(it.next());
}
//alert(it.next()); // A StopIteration exception is thrown
The iterable protocol In order to be iterable, an object must implement the @@iterator method, meaning that the object (or one of the objects up its prototype chain) must have a property with a @@iterator key which is available via constant Symbol.
Iterator is an object which allows us to access a collection of objects one at a time. The following built-in types are by default iterable − String. Array. Map.
JavaScript supports looping through data set objects such as arrays using control structures such as for…of and the spread operator ... . This is referred to as the iterable and the data structures that support this functionality are called iterables.
Generators are a special type of function in JavaScript that can pause and resume state. A Generator function returns an iterator, which can be used to stop the function in the middle, do something, and then resume it whenever.
Ok, the enumerable pattern is not a real iterator then.
Is this (below) useful for you? It conforms to the sematics you gave at least. As usual there are tradeoffs to be made here and there, and I didn't think very hard when deciding this time :).
And maybe you would like to be able to send in a number or two and iterate over a range in that way. But this could maybe be a start (there's support for iterating over hashes, arrays and strings).
It's a whole demo page which runs itself and does some debug output, but the (possibly) interesting stuff is in the
window.npup = (function() {
[...]
})();
spot.
Maybe it is just me who doesn't get it at all, but what would you use such a java-like Iterator for in a real situation?
Best /npup
<html>
<head>
<title>untitled</title>
</head>
<body>
<ul id="output"></ul>
<script type="text/javascript">
window.log = (function (outputAreaId) {
var myConsole = document.getElementById(outputAreaId);
function createElem(color) {
var elem = document.createElement('li');
elem.style.color = color;
return elem;
}
function appendElem(elem) {
myConsole.appendChild(elem);
}
function debug(msg) {
var elem = createElem('#888');
elem.innerHTML = msg;
appendElem(elem);
}
function error(msg) {
var elem = createElem('#f88');
elem.innerHTML = msg;
appendElem(elem);
}
return {
debug: debug
, error: error
};
})('output');
window.npup = (function () {
// Array check as proposed by Mr. Crockford
function isArray(candidate) {
return candidate &&
typeof candidate==='object' &&
typeof candidate.length === 'number' &&
typeof candidate.splice === 'function' &&
!(candidate.propertyIsEnumerable('length'));
}
function dontIterate(collection) {
// put some checks chere for stuff that isn't iterable (yet)
return (!collection || typeof collection==='number' || typeof collection==='boolean');
}
function Iterator(collection) {
if (typeof collection==='string') {collection = collection.split('');}
if (dontIterate(collection)) {throw new Error('Oh you nasty man, I won\'t iterate over that ('+collection+')!');}
var arr = isArray(collection);
var idx = 0, top=0;
var keys = [], prop;
if (arr) {top = collection.length;}
else {for (prop in collection) {keys.push(prop);}}
this.next = function () {
if (!this.hasNext()) {throw new Error('Oh you nasty man. I have no more elements.');}
var elem = arr ? collection[idx] : {key:keys[idx], value:collection[keys[idx]]};
++idx;
return elem;
};
this.hasNext = function () {return arr ? idx<=top : idx<=keys.length;};
}
return {Iterator: Iterator};
})();
var element;
log.debug('--- Hash demo');
var o = {foo:1, bar:2, baz:3, bork:4, hepp: {a:1,b:2,c:3}, bluff:666, bluff2:777};
var iterator = new npup.Iterator(o);
for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
log.debug('got elem from hash: '+element.key+' => '+element.value);
if (typeof element.value==='object') {
var i2 = new npup.Iterator(element.value);
for (var e2=i2.next(); i2.hasNext(); e2=i2.next()) {
log.debug(' # from inner hash: '+e2.key+' => '+e2.value);
}
}
}
log.debug('--- Array demo');
var a = [1,2,3,42,666,777];
iterator = new npup.Iterator(a);
for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
log.debug('got elem from array: '+ element);
}
log.debug('--- String demo');
var s = 'First the pants, THEN the shoes!';
iterator = new npup.Iterator(s);
for (element = iterator.next(); iterator.hasNext(); element = iterator.next()) {
log.debug('got elem from string: '+ element);
}
log.debug('--- Emptiness demo');
try {
log.debug('Try to get next..');
var boogie = iterator.next();
}
catch(e) {
log.error('OW: '+e);
}
log.debug('--- Non iterables demo');
try{iterator = new npup.Iterator(true);} catch(e) {log.error('iterate over boolean: '+e);}
try{iterator = new npup.Iterator(6);} catch(e) {log.error('iterate over number: '+e);}
try{iterator = new npup.Iterator(null);} catch(e) {log.error('iterate over null: '+e);}
try{iterator = new npup.Iterator();} catch(e) {log.error('iterate over undefined: '+e);}
</script>
</body>
</html>
JQuery has the each() method: http://api.jquery.com/jQuery.each/
but probably there's something similar even in other libraries such as Moo or Dojo.
Javascript 1.7 implements the Iterator function: https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Iterators_and_Generators
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With