5 See <http://mochikit.com/> for documentation, downloads, license, etc.
7 (c) 2005 Bob Ippolito. All rights Reserved.
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Iter');
13 dojo.require('MochiKit.Base');
16 if (typeof(JSAN) != 'undefined') {
17 JSAN.use("MochiKit.Base", []);
21 if (typeof(MochiKit.Base) == 'undefined') {
25 throw "MochiKit.Iter depends on MochiKit.Base!";
28 if (typeof(MochiKit.Iter) == 'undefined') {
32 MochiKit.Iter.NAME = "MochiKit.Iter";
33 MochiKit.Iter.VERSION = "1.4";
34 MochiKit.Base.update(MochiKit.Iter, {
35 __repr__: function () {
36 return "[" + this.NAME + " " + this.VERSION + "]";
38 toString: function () {
39 return this.__repr__();
42 /** @id MochiKit.Iter.registerIteratorFactory */
43 registerIteratorFactory: function (name, check, iterfactory, /* optional */ override) {
44 MochiKit.Iter.iteratorRegistry.register(name, check, iterfactory, override);
47 /** @id MochiKit.Iter.iter */
48 iter: function (iterable, /* optional */ sentinel) {
49 var self = MochiKit.Iter;
50 if (arguments.length == 2) {
51 return self.takewhile(
52 function (a) { return a != sentinel; },
56 if (typeof(iterable.next) == 'function') {
58 } else if (typeof(iterable.iter) == 'function') {
59 return iterable.iter();
61 } else if (typeof(iterable.__iterator__) == 'function') {
63 // XXX: We can't support JavaScript 1.7 __iterator__ directly
64 // because of Object.prototype.__iterator__
66 return iterable.__iterator__();
71 return self.iteratorRegistry.match(iterable);
73 var m = MochiKit.Base;
74 if (e == m.NotFound) {
75 e = new TypeError(typeof(iterable) + ": " + m.repr(iterable) + " is not iterable");
81 /** @id MochiKit.Iter.count */
86 var m = MochiKit.Base;
88 repr: function () { return "count(" + n + ")"; },
89 toString: m.forwardCall("repr"),
94 /** @id MochiKit.Iter.cycle */
96 var self = MochiKit.Iter;
97 var m = MochiKit.Base;
99 var iterator = self.iter(p);
101 repr: function () { return "cycle(...)"; },
102 toString: m.forwardCall("repr"),
105 var rval = iterator.next();
109 if (e != self.StopIteration) {
112 if (lst.length === 0) {
113 this.next = function () {
114 throw self.StopIteration;
118 this.next = function () {
119 i = (i + 1) % lst.length;
129 /** @id MochiKit.Iter.repeat */
130 repeat: function (elem, /* optional */n) {
131 var m = MochiKit.Base;
132 if (typeof(n) == 'undefined') {
135 return "repeat(" + m.repr(elem) + ")";
137 toString: m.forwardCall("repr"),
145 return "repeat(" + m.repr(elem) + ", " + n + ")";
147 toString: m.forwardCall("repr"),
150 throw MochiKit.Iter.StopIteration;
158 /** @id MochiKit.Iter.next */
159 next: function (iterator) {
160 return iterator.next();
163 /** @id MochiKit.Iter.izip */
164 izip: function (p, q/*, ...*/) {
165 var m = MochiKit.Base;
166 var self = MochiKit.Iter;
167 var next = self.next;
168 var iterables = m.map(self.iter, arguments);
170 repr: function () { return "izip(...)"; },
171 toString: m.forwardCall("repr"),
172 next: function () { return m.map(next, iterables); }
176 /** @id MochiKit.Iter.ifilter */
177 ifilter: function (pred, seq) {
178 var m = MochiKit.Base;
179 seq = MochiKit.Iter.iter(seq);
181 pred = m.operator.truth;
184 repr: function () { return "ifilter(...)"; },
185 toString: m.forwardCall("repr"),
188 var rval = seq.next();
193 // mozilla warnings aren't too bright
199 /** @id MochiKit.Iter.ifilterfalse */
200 ifilterfalse: function (pred, seq) {
201 var m = MochiKit.Base;
202 seq = MochiKit.Iter.iter(seq);
204 pred = m.operator.truth;
207 repr: function () { return "ifilterfalse(...)"; },
208 toString: m.forwardCall("repr"),
211 var rval = seq.next();
216 // mozilla warnings aren't too bright
222 /** @id MochiKit.Iter.islice */
223 islice: function (seq/*, [start,] stop[, step] */) {
224 var self = MochiKit.Iter;
225 var m = MochiKit.Base;
226 seq = self.iter(seq);
231 if (arguments.length == 2) {
233 } else if (arguments.length == 3) {
234 start = arguments[1];
237 start = arguments[1];
243 return "islice(" + ["...", start, stop, step].join(", ") + ")";
245 toString: m.forwardCall("repr"),
253 throw self.StopIteration;
261 /** @id MochiKit.Iter.imap */
262 imap: function (fun, p, q/*, ...*/) {
263 var m = MochiKit.Base;
264 var self = MochiKit.Iter;
265 var iterables = m.map(self.iter, m.extend(null, arguments, 1));
267 var next = self.next;
269 repr: function () { return "imap(...)"; },
270 toString: m.forwardCall("repr"),
272 return fun.apply(this, map(next, iterables));
277 /** @id MochiKit.Iter.applymap */
278 applymap: function (fun, seq, self) {
279 seq = MochiKit.Iter.iter(seq);
280 var m = MochiKit.Base;
282 repr: function () { return "applymap(...)"; },
283 toString: m.forwardCall("repr"),
285 return fun.apply(self, seq.next());
290 /** @id MochiKit.Iter.chain */
291 chain: function (p, q/*, ...*/) {
293 var self = MochiKit.Iter;
294 var m = MochiKit.Base;
295 if (arguments.length == 1) {
296 return self.iter(arguments[0]);
298 var argiter = m.map(self.iter, arguments);
300 repr: function () { return "chain(...)"; },
301 toString: m.forwardCall("repr"),
303 while (argiter.length > 1) {
305 return argiter[0].next();
307 if (e != self.StopIteration) {
313 if (argiter.length == 1) {
314 // optimize last element
315 var arg = argiter.shift();
316 this.next = m.bind("next", arg);
319 throw self.StopIteration;
324 /** @id MochiKit.Iter.takewhile */
325 takewhile: function (pred, seq) {
326 var self = MochiKit.Iter;
327 seq = self.iter(seq);
329 repr: function () { return "takewhile(...)"; },
330 toString: MochiKit.Base.forwardCall("repr"),
332 var rval = seq.next();
334 this.next = function () {
335 throw self.StopIteration;
344 /** @id MochiKit.Iter.dropwhile */
345 dropwhile: function (pred, seq) {
346 seq = MochiKit.Iter.iter(seq);
347 var m = MochiKit.Base;
350 "repr": function () { return "dropwhile(...)"; },
351 "toString": m.forwardCall("repr"),
352 "next": function () {
354 var rval = seq.next();
359 this.next = bind("next", seq);
365 _tee: function (ident, sync, iterable) {
366 sync.pos[ident] = -1;
367 var m = MochiKit.Base;
368 var listMin = m.listMin;
370 repr: function () { return "tee(" + ident + ", ...)"; },
371 toString: m.forwardCall("repr"),
374 var i = sync.pos[ident];
377 rval = iterable.next();
378 sync.deque.push(rval);
380 sync.pos[ident] += 1;
382 rval = sync.deque[i - sync.min];
383 sync.pos[ident] += 1;
384 if (i == sync.min && listMin(sync.pos) != sync.min) {
394 /** @id MochiKit.Iter.tee */
395 tee: function (iterable, n/* = 2 */) {
403 if (arguments.length == 1 || typeof(n) == "undefined" || n === null) {
406 var self = MochiKit.Iter;
407 iterable = self.iter(iterable);
408 var _tee = self._tee;
409 for (var i = 0; i < n; i++) {
410 rval.push(_tee(i, sync, iterable));
415 /** @id MochiKit.Iter.list */
416 list: function (iterable) {
417 // Fast-path for Array and Array-like
419 if (iterable instanceof Array) {
420 return iterable.slice();
422 // this is necessary to avoid a Safari crash
423 if (typeof(iterable) == "function" &&
424 !(iterable instanceof Function) &&
425 typeof(iterable.length) == 'number') {
427 for (var i = 0; i < iterable.length; i++) {
428 rval.push(iterable[i]);
433 var self = MochiKit.Iter;
434 iterable = self.iter(iterable);
438 rval.push(iterable.next());
441 if (e != self.StopIteration) {
446 // mozilla warnings aren't too bright
451 /** @id MochiKit.Iter.reduce */
452 reduce: function (fn, iterable, /* optional */initial) {
455 var self = MochiKit.Iter;
456 iterable = self.iter(iterable);
457 if (arguments.length < 3) {
461 if (e == self.StopIteration) {
462 e = new TypeError("reduce() of empty sequence with no initial value");
470 x = fn(x, iterable.next());
473 if (e != self.StopIteration) {
480 /** @id MochiKit.Iter.range */
481 range: function (/* [start,] stop[, step] */) {
485 if (arguments.length == 1) {
487 } else if (arguments.length == 2) {
488 start = arguments[0];
490 } else if (arguments.length == 3) {
491 start = arguments[0];
495 throw new TypeError("range() takes 1, 2, or 3 arguments!");
498 throw new TypeError("range() step must not be 0");
502 if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
503 throw MochiKit.Iter.StopIteration;
510 return "range(" + [start, stop, step].join(", ") + ")";
512 toString: MochiKit.Base.forwardCall("repr")
516 /** @id MochiKit.Iter.sum */
517 sum: function (iterable, start/* = 0 */) {
518 if (typeof(start) == "undefined" || start === null) {
522 var self = MochiKit.Iter;
523 iterable = self.iter(iterable);
526 x += iterable.next();
529 if (e != self.StopIteration) {
536 /** @id MochiKit.Iter.exhaust */
537 exhaust: function (iterable) {
538 var self = MochiKit.Iter;
539 iterable = self.iter(iterable);
545 if (e != self.StopIteration) {
551 /** @id MochiKit.Iter.forEach */
552 forEach: function (iterable, func, /* optional */self) {
553 var m = MochiKit.Base;
554 if (arguments.length > 2) {
555 func = m.bind(func, self);
557 // fast path for array
558 if (m.isArrayLike(iterable)) {
560 for (var i = 0; i < iterable.length; i++) {
564 if (e != MochiKit.Iter.StopIteration) {
569 self = MochiKit.Iter;
570 self.exhaust(self.imap(func, iterable));
574 /** @id MochiKit.Iter.every */
575 every: function (iterable, func) {
576 var self = MochiKit.Iter;
578 self.ifilterfalse(func, iterable).next();
581 if (e != self.StopIteration) {
588 /** @id MochiKit.Iter.sorted */
589 sorted: function (iterable, /* optional */cmp) {
590 var rval = MochiKit.Iter.list(iterable);
591 if (arguments.length == 1) {
592 cmp = MochiKit.Base.compare;
598 /** @id MochiKit.Iter.reversed */
599 reversed: function (iterable) {
600 var rval = MochiKit.Iter.list(iterable);
605 /** @id MochiKit.Iter.some */
606 some: function (iterable, func) {
607 var self = MochiKit.Iter;
609 self.ifilter(func, iterable).next();
612 if (e != self.StopIteration) {
619 /** @id MochiKit.Iter.iextend */
620 iextend: function (lst, iterable) {
621 if (MochiKit.Base.isArrayLike(iterable)) {
622 // fast-path for array-like
623 for (var i = 0; i < iterable.length; i++) {
624 lst.push(iterable[i]);
627 var self = MochiKit.Iter;
628 iterable = self.iter(iterable);
631 lst.push(iterable.next());
634 if (e != self.StopIteration) {
642 /** @id MochiKit.Iter.groupby */
643 groupby: function(iterable, /* optional */ keyfunc) {
644 var m = MochiKit.Base;
645 var self = MochiKit.Iter;
646 if (arguments.length < 2) {
647 keyfunc = m.operator.identity;
649 iterable = self.iter(iterable);
668 var compare = m.compare;
670 repr: function () { return "groupby(...)"; },
674 // iterate until meet next group
675 while (compare(k, pk) === 0) {
686 if (v == undefined) { // Is there something to eat?
689 if (compare(k, pk) !== 0) {
690 throw self.StopIteration;
699 /** @id MochiKit.Iter.groupby_as_array */
700 groupby_as_array: function (iterable, /* optional */ keyfunc) {
701 var m = MochiKit.Base;
702 var self = MochiKit.Iter;
703 if (arguments.length < 2) {
704 keyfunc = m.operator.identity;
707 iterable = self.iter(iterable);
711 var compare = m.compare;
714 var value = iterable.next();
715 var key = keyfunc(value);
717 if (e == self.StopIteration) {
722 if (first || compare(key, prev_key) !== 0) {
724 result.push([key, values]);
733 /** @id MochiKit.Iter.arrayLikeIter */
734 arrayLikeIter: function (iterable) {
737 repr: function () { return "arrayLikeIter(...)"; },
738 toString: MochiKit.Base.forwardCall("repr"),
740 if (i >= iterable.length) {
741 throw MochiKit.Iter.StopIteration;
743 return iterable[i++];
748 /** @id MochiKit.Iter.hasIterateNext */
749 hasIterateNext: function (iterable) {
750 return (iterable && typeof(iterable.iterateNext) == "function");
753 /** @id MochiKit.Iter.iterateNextIter */
754 iterateNextIter: function (iterable) {
756 repr: function () { return "iterateNextIter(...)"; },
757 toString: MochiKit.Base.forwardCall("repr"),
759 var rval = iterable.iterateNext();
760 if (rval === null || rval === undefined) {
761 throw MochiKit.Iter.StopIteration;
770 MochiKit.Iter.EXPORT_OK = [
777 MochiKit.Iter.EXPORT = [
779 "registerIteratorFactory",
810 MochiKit.Iter.__new__ = function () {
811 var m = MochiKit.Base;
812 // Re-use StopIteration if exists (e.g. SpiderMonkey)
813 if (typeof(StopIteration) != "undefined") {
814 this.StopIteration = StopIteration;
816 /** @id MochiKit.Iter.StopIteration */
817 this.StopIteration = new m.NamedError("StopIteration");
819 this.iteratorRegistry = new m.AdapterRegistry();
820 // Register the iterator factory for arrays
821 this.registerIteratorFactory(
827 this.registerIteratorFactory(
834 ":common": this.EXPORT,
835 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
838 m.nameFunctions(this);
842 MochiKit.Iter.__new__();
845 // XXX: Internet Explorer blows
847 if (MochiKit.__export__) {
848 reduce = MochiKit.Iter.reduce;
851 MochiKit.Base._exportSymbols(this, MochiKit.Iter);