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
418 var m = MochiKit.Base;
419 if (typeof(iterable.slice) == 'function') {
420 return iterable.slice();
421 } else if (m.isArrayLike(iterable)) {
422 return m.concat(iterable);
425 var self = MochiKit.Iter;
426 iterable = self.iter(iterable);
430 rval.push(iterable.next());
433 if (e != self.StopIteration) {
438 // mozilla warnings aren't too bright
443 /** @id MochiKit.Iter.reduce */
444 reduce: function (fn, iterable, /* optional */initial) {
447 var self = MochiKit.Iter;
448 iterable = self.iter(iterable);
449 if (arguments.length < 3) {
453 if (e == self.StopIteration) {
454 e = new TypeError("reduce() of empty sequence with no initial value");
462 x = fn(x, iterable.next());
465 if (e != self.StopIteration) {
472 /** @id MochiKit.Iter.range */
473 range: function (/* [start,] stop[, step] */) {
477 if (arguments.length == 1) {
479 } else if (arguments.length == 2) {
480 start = arguments[0];
482 } else if (arguments.length == 3) {
483 start = arguments[0];
487 throw new TypeError("range() takes 1, 2, or 3 arguments!");
490 throw new TypeError("range() step must not be 0");
494 if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
495 throw MochiKit.Iter.StopIteration;
502 return "range(" + [start, stop, step].join(", ") + ")";
504 toString: MochiKit.Base.forwardCall("repr")
508 /** @id MochiKit.Iter.sum */
509 sum: function (iterable, start/* = 0 */) {
510 if (typeof(start) == "undefined" || start === null) {
514 var self = MochiKit.Iter;
515 iterable = self.iter(iterable);
518 x += iterable.next();
521 if (e != self.StopIteration) {
528 /** @id MochiKit.Iter.exhaust */
529 exhaust: function (iterable) {
530 var self = MochiKit.Iter;
531 iterable = self.iter(iterable);
537 if (e != self.StopIteration) {
543 /** @id MochiKit.Iter.forEach */
544 forEach: function (iterable, func, /* optional */self) {
545 var m = MochiKit.Base;
546 if (arguments.length > 2) {
547 func = m.bind(func, self);
549 // fast path for array
550 if (m.isArrayLike(iterable)) {
552 for (var i = 0; i < iterable.length; i++) {
556 if (e != MochiKit.Iter.StopIteration) {
561 self = MochiKit.Iter;
562 self.exhaust(self.imap(func, iterable));
566 /** @id MochiKit.Iter.every */
567 every: function (iterable, func) {
568 var self = MochiKit.Iter;
570 self.ifilterfalse(func, iterable).next();
573 if (e != self.StopIteration) {
580 /** @id MochiKit.Iter.sorted */
581 sorted: function (iterable, /* optional */cmp) {
582 var rval = MochiKit.Iter.list(iterable);
583 if (arguments.length == 1) {
584 cmp = MochiKit.Base.compare;
590 /** @id MochiKit.Iter.reversed */
591 reversed: function (iterable) {
592 var rval = MochiKit.Iter.list(iterable);
597 /** @id MochiKit.Iter.some */
598 some: function (iterable, func) {
599 var self = MochiKit.Iter;
601 self.ifilter(func, iterable).next();
604 if (e != self.StopIteration) {
611 /** @id MochiKit.Iter.iextend */
612 iextend: function (lst, iterable) {
613 if (MochiKit.Base.isArrayLike(iterable)) {
614 // fast-path for array-like
615 for (var i = 0; i < iterable.length; i++) {
616 lst.push(iterable[i]);
619 var self = MochiKit.Iter;
620 iterable = self.iter(iterable);
623 lst.push(iterable.next());
626 if (e != self.StopIteration) {
634 /** @id MochiKit.Iter.groupby */
635 groupby: function(iterable, /* optional */ keyfunc) {
636 var m = MochiKit.Base;
637 var self = MochiKit.Iter;
638 if (arguments.length < 2) {
639 keyfunc = m.operator.identity;
641 iterable = self.iter(iterable);
660 var compare = m.compare;
662 repr: function () { return "groupby(...)"; },
666 // iterate until meet next group
667 while (compare(k, pk) === 0) {
678 if (v == undefined) { // Is there something to eat?
681 if (compare(k, pk) !== 0) {
682 throw self.StopIteration;
691 /** @id MochiKit.Iter.groupby_as_array */
692 groupby_as_array: function (iterable, /* optional */ keyfunc) {
693 var m = MochiKit.Base;
694 var self = MochiKit.Iter;
695 if (arguments.length < 2) {
696 keyfunc = m.operator.identity;
699 iterable = self.iter(iterable);
703 var compare = m.compare;
706 var value = iterable.next();
707 var key = keyfunc(value);
709 if (e == self.StopIteration) {
714 if (first || compare(key, prev_key) !== 0) {
716 result.push([key, values]);
725 /** @id MochiKit.Iter.arrayLikeIter */
726 arrayLikeIter: function (iterable) {
729 repr: function () { return "arrayLikeIter(...)"; },
730 toString: MochiKit.Base.forwardCall("repr"),
732 if (i >= iterable.length) {
733 throw MochiKit.Iter.StopIteration;
735 return iterable[i++];
740 /** @id MochiKit.Iter.hasIterateNext */
741 hasIterateNext: function (iterable) {
742 return (iterable && typeof(iterable.iterateNext) == "function");
745 /** @id MochiKit.Iter.iterateNextIter */
746 iterateNextIter: function (iterable) {
748 repr: function () { return "iterateNextIter(...)"; },
749 toString: MochiKit.Base.forwardCall("repr"),
751 var rval = iterable.iterateNext();
752 if (rval === null || rval === undefined) {
753 throw MochiKit.Iter.StopIteration;
762 MochiKit.Iter.EXPORT_OK = [
769 MochiKit.Iter.EXPORT = [
771 "registerIteratorFactory",
802 MochiKit.Iter.__new__ = function () {
803 var m = MochiKit.Base;
804 // Re-use StopIteration if exists (e.g. SpiderMonkey)
805 if (typeof(StopIteration) != "undefined") {
806 this.StopIteration = StopIteration;
808 /** @id MochiKit.Iter.StopIteration */
809 this.StopIteration = new m.NamedError("StopIteration");
811 this.iteratorRegistry = new m.AdapterRegistry();
812 // Register the iterator factory for arrays
813 this.registerIteratorFactory(
819 this.registerIteratorFactory(
826 ":common": this.EXPORT,
827 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
830 m.nameFunctions(this);
834 MochiKit.Iter.__new__();
837 // XXX: Internet Explorer blows
839 if (MochiKit.__export__) {
840 reduce = MochiKit.Iter.reduce;
843 MochiKit.Base._exportSymbols(this, MochiKit.Iter);