c2fcbeee05b462288c7010c1d8f6bf9a0695a8e1
[dygraphs.git] / mochikit_v14 / MochiKit / Iter.js
1 /***
2
3 MochiKit.Iter 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide('MochiKit.Iter');
13 dojo.require('MochiKit.Base');
14 }
15
16 if (typeof(JSAN) != 'undefined') {
17 JSAN.use("MochiKit.Base", []);
18 }
19
20 try {
21 if (typeof(MochiKit.Base) == 'undefined') {
22 throw "";
23 }
24 } catch (e) {
25 throw "MochiKit.Iter depends on MochiKit.Base!";
26 }
27
28 if (typeof(MochiKit.Iter) == 'undefined') {
29 MochiKit.Iter = {};
30 }
31
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 + "]";
37 },
38 toString: function () {
39 return this.__repr__();
40 },
41
42 /** @id MochiKit.Iter.registerIteratorFactory */
43 registerIteratorFactory: function (name, check, iterfactory, /* optional */ override) {
44 MochiKit.Iter.iteratorRegistry.register(name, check, iterfactory, override);
45 },
46
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; },
53 iterable
54 );
55 }
56 if (typeof(iterable.next) == 'function') {
57 return iterable;
58 } else if (typeof(iterable.iter) == 'function') {
59 return iterable.iter();
60 /*
61 } else if (typeof(iterable.__iterator__) == 'function') {
62 //
63 // XXX: We can't support JavaScript 1.7 __iterator__ directly
64 // because of Object.prototype.__iterator__
65 //
66 return iterable.__iterator__();
67 */
68 }
69
70 try {
71 return self.iteratorRegistry.match(iterable);
72 } catch (e) {
73 var m = MochiKit.Base;
74 if (e == m.NotFound) {
75 e = new TypeError(typeof(iterable) + ": " + m.repr(iterable) + " is not iterable");
76 }
77 throw e;
78 }
79 },
80
81 /** @id MochiKit.Iter.count */
82 count: function (n) {
83 if (!n) {
84 n = 0;
85 }
86 var m = MochiKit.Base;
87 return {
88 repr: function () { return "count(" + n + ")"; },
89 toString: m.forwardCall("repr"),
90 next: m.counter(n)
91 };
92 },
93
94 /** @id MochiKit.Iter.cycle */
95 cycle: function (p) {
96 var self = MochiKit.Iter;
97 var m = MochiKit.Base;
98 var lst = [];
99 var iterator = self.iter(p);
100 return {
101 repr: function () { return "cycle(...)"; },
102 toString: m.forwardCall("repr"),
103 next: function () {
104 try {
105 var rval = iterator.next();
106 lst.push(rval);
107 return rval;
108 } catch (e) {
109 if (e != self.StopIteration) {
110 throw e;
111 }
112 if (lst.length === 0) {
113 this.next = function () {
114 throw self.StopIteration;
115 };
116 } else {
117 var i = -1;
118 this.next = function () {
119 i = (i + 1) % lst.length;
120 return lst[i];
121 };
122 }
123 return this.next();
124 }
125 }
126 };
127 },
128
129 /** @id MochiKit.Iter.repeat */
130 repeat: function (elem, /* optional */n) {
131 var m = MochiKit.Base;
132 if (typeof(n) == 'undefined') {
133 return {
134 repr: function () {
135 return "repeat(" + m.repr(elem) + ")";
136 },
137 toString: m.forwardCall("repr"),
138 next: function () {
139 return elem;
140 }
141 };
142 }
143 return {
144 repr: function () {
145 return "repeat(" + m.repr(elem) + ", " + n + ")";
146 },
147 toString: m.forwardCall("repr"),
148 next: function () {
149 if (n <= 0) {
150 throw MochiKit.Iter.StopIteration;
151 }
152 n -= 1;
153 return elem;
154 }
155 };
156 },
157
158 /** @id MochiKit.Iter.next */
159 next: function (iterator) {
160 return iterator.next();
161 },
162
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);
169 return {
170 repr: function () { return "izip(...)"; },
171 toString: m.forwardCall("repr"),
172 next: function () { return m.map(next, iterables); }
173 };
174 },
175
176 /** @id MochiKit.Iter.ifilter */
177 ifilter: function (pred, seq) {
178 var m = MochiKit.Base;
179 seq = MochiKit.Iter.iter(seq);
180 if (pred === null) {
181 pred = m.operator.truth;
182 }
183 return {
184 repr: function () { return "ifilter(...)"; },
185 toString: m.forwardCall("repr"),
186 next: function () {
187 while (true) {
188 var rval = seq.next();
189 if (pred(rval)) {
190 return rval;
191 }
192 }
193 // mozilla warnings aren't too bright
194 return undefined;
195 }
196 };
197 },
198
199 /** @id MochiKit.Iter.ifilterfalse */
200 ifilterfalse: function (pred, seq) {
201 var m = MochiKit.Base;
202 seq = MochiKit.Iter.iter(seq);
203 if (pred === null) {
204 pred = m.operator.truth;
205 }
206 return {
207 repr: function () { return "ifilterfalse(...)"; },
208 toString: m.forwardCall("repr"),
209 next: function () {
210 while (true) {
211 var rval = seq.next();
212 if (!pred(rval)) {
213 return rval;
214 }
215 }
216 // mozilla warnings aren't too bright
217 return undefined;
218 }
219 };
220 },
221
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);
227 var start = 0;
228 var stop = 0;
229 var step = 1;
230 var i = -1;
231 if (arguments.length == 2) {
232 stop = arguments[1];
233 } else if (arguments.length == 3) {
234 start = arguments[1];
235 stop = arguments[2];
236 } else {
237 start = arguments[1];
238 stop = arguments[2];
239 step = arguments[3];
240 }
241 return {
242 repr: function () {
243 return "islice(" + ["...", start, stop, step].join(", ") + ")";
244 },
245 toString: m.forwardCall("repr"),
246 next: function () {
247 var rval;
248 while (i < start) {
249 rval = seq.next();
250 i++;
251 }
252 if (start >= stop) {
253 throw self.StopIteration;
254 }
255 start += step;
256 return rval;
257 }
258 };
259 },
260
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));
266 var map = m.map;
267 var next = self.next;
268 return {
269 repr: function () { return "imap(...)"; },
270 toString: m.forwardCall("repr"),
271 next: function () {
272 return fun.apply(this, map(next, iterables));
273 }
274 };
275 },
276
277 /** @id MochiKit.Iter.applymap */
278 applymap: function (fun, seq, self) {
279 seq = MochiKit.Iter.iter(seq);
280 var m = MochiKit.Base;
281 return {
282 repr: function () { return "applymap(...)"; },
283 toString: m.forwardCall("repr"),
284 next: function () {
285 return fun.apply(self, seq.next());
286 }
287 };
288 },
289
290 /** @id MochiKit.Iter.chain */
291 chain: function (p, q/*, ...*/) {
292 // dumb fast path
293 var self = MochiKit.Iter;
294 var m = MochiKit.Base;
295 if (arguments.length == 1) {
296 return self.iter(arguments[0]);
297 }
298 var argiter = m.map(self.iter, arguments);
299 return {
300 repr: function () { return "chain(...)"; },
301 toString: m.forwardCall("repr"),
302 next: function () {
303 while (argiter.length > 1) {
304 try {
305 return argiter[0].next();
306 } catch (e) {
307 if (e != self.StopIteration) {
308 throw e;
309 }
310 argiter.shift();
311 }
312 }
313 if (argiter.length == 1) {
314 // optimize last element
315 var arg = argiter.shift();
316 this.next = m.bind("next", arg);
317 return this.next();
318 }
319 throw self.StopIteration;
320 }
321 };
322 },
323
324 /** @id MochiKit.Iter.takewhile */
325 takewhile: function (pred, seq) {
326 var self = MochiKit.Iter;
327 seq = self.iter(seq);
328 return {
329 repr: function () { return "takewhile(...)"; },
330 toString: MochiKit.Base.forwardCall("repr"),
331 next: function () {
332 var rval = seq.next();
333 if (!pred(rval)) {
334 this.next = function () {
335 throw self.StopIteration;
336 };
337 this.next();
338 }
339 return rval;
340 }
341 };
342 },
343
344 /** @id MochiKit.Iter.dropwhile */
345 dropwhile: function (pred, seq) {
346 seq = MochiKit.Iter.iter(seq);
347 var m = MochiKit.Base;
348 var bind = m.bind;
349 return {
350 "repr": function () { return "dropwhile(...)"; },
351 "toString": m.forwardCall("repr"),
352 "next": function () {
353 while (true) {
354 var rval = seq.next();
355 if (!pred(rval)) {
356 break;
357 }
358 }
359 this.next = bind("next", seq);
360 return rval;
361 }
362 };
363 },
364
365 _tee: function (ident, sync, iterable) {
366 sync.pos[ident] = -1;
367 var m = MochiKit.Base;
368 var listMin = m.listMin;
369 return {
370 repr: function () { return "tee(" + ident + ", ...)"; },
371 toString: m.forwardCall("repr"),
372 next: function () {
373 var rval;
374 var i = sync.pos[ident];
375
376 if (i == sync.max) {
377 rval = iterable.next();
378 sync.deque.push(rval);
379 sync.max += 1;
380 sync.pos[ident] += 1;
381 } else {
382 rval = sync.deque[i - sync.min];
383 sync.pos[ident] += 1;
384 if (i == sync.min && listMin(sync.pos) != sync.min) {
385 sync.min += 1;
386 sync.deque.shift();
387 }
388 }
389 return rval;
390 }
391 };
392 },
393
394 /** @id MochiKit.Iter.tee */
395 tee: function (iterable, n/* = 2 */) {
396 var rval = [];
397 var sync = {
398 "pos": [],
399 "deque": [],
400 "max": -1,
401 "min": -1
402 };
403 if (arguments.length == 1 || typeof(n) == "undefined" || n === null) {
404 n = 2;
405 }
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));
411 }
412 return rval;
413 },
414
415 /** @id MochiKit.Iter.list */
416 list: function (iterable) {
417 // Fast-path for Array and Array-like
418 var rval;
419 if (iterable instanceof Array) {
420 return iterable.slice();
421 }
422 // this is necessary to avoid a Safari crash
423 if (typeof(iterable) == "function" &&
424 !(iterable instanceof Function) &&
425 typeof(iterable.length) == 'number') {
426 rval = [];
427 for (var i = 0; i < iterable.length; i++) {
428 rval.push(iterable[i]);
429 }
430 return rval;
431 }
432
433 var self = MochiKit.Iter;
434 iterable = self.iter(iterable);
435 var rval = [];
436 try {
437 while (true) {
438 rval.push(iterable.next());
439 }
440 } catch (e) {
441 if (e != self.StopIteration) {
442 throw e;
443 }
444 return rval;
445 }
446 // mozilla warnings aren't too bright
447 return undefined;
448 },
449
450
451 /** @id MochiKit.Iter.reduce */
452 reduce: function (fn, iterable, /* optional */initial) {
453 var i = 0;
454 var x = initial;
455 var self = MochiKit.Iter;
456 iterable = self.iter(iterable);
457 if (arguments.length < 3) {
458 try {
459 x = iterable.next();
460 } catch (e) {
461 if (e == self.StopIteration) {
462 e = new TypeError("reduce() of empty sequence with no initial value");
463 }
464 throw e;
465 }
466 i++;
467 }
468 try {
469 while (true) {
470 x = fn(x, iterable.next());
471 }
472 } catch (e) {
473 if (e != self.StopIteration) {
474 throw e;
475 }
476 }
477 return x;
478 },
479
480 /** @id MochiKit.Iter.range */
481 range: function (/* [start,] stop[, step] */) {
482 var start = 0;
483 var stop = 0;
484 var step = 1;
485 if (arguments.length == 1) {
486 stop = arguments[0];
487 } else if (arguments.length == 2) {
488 start = arguments[0];
489 stop = arguments[1];
490 } else if (arguments.length == 3) {
491 start = arguments[0];
492 stop = arguments[1];
493 step = arguments[2];
494 } else {
495 throw new TypeError("range() takes 1, 2, or 3 arguments!");
496 }
497 if (step === 0) {
498 throw new TypeError("range() step must not be 0");
499 }
500 return {
501 next: function () {
502 if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
503 throw MochiKit.Iter.StopIteration;
504 }
505 var rval = start;
506 start += step;
507 return rval;
508 },
509 repr: function () {
510 return "range(" + [start, stop, step].join(", ") + ")";
511 },
512 toString: MochiKit.Base.forwardCall("repr")
513 };
514 },
515
516 /** @id MochiKit.Iter.sum */
517 sum: function (iterable, start/* = 0 */) {
518 if (typeof(start) == "undefined" || start === null) {
519 start = 0;
520 }
521 var x = start;
522 var self = MochiKit.Iter;
523 iterable = self.iter(iterable);
524 try {
525 while (true) {
526 x += iterable.next();
527 }
528 } catch (e) {
529 if (e != self.StopIteration) {
530 throw e;
531 }
532 }
533 return x;
534 },
535
536 /** @id MochiKit.Iter.exhaust */
537 exhaust: function (iterable) {
538 var self = MochiKit.Iter;
539 iterable = self.iter(iterable);
540 try {
541 while (true) {
542 iterable.next();
543 }
544 } catch (e) {
545 if (e != self.StopIteration) {
546 throw e;
547 }
548 }
549 },
550
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);
556 }
557 // fast path for array
558 if (m.isArrayLike(iterable)) {
559 try {
560 for (var i = 0; i < iterable.length; i++) {
561 func(iterable[i]);
562 }
563 } catch (e) {
564 if (e != MochiKit.Iter.StopIteration) {
565 throw e;
566 }
567 }
568 } else {
569 self = MochiKit.Iter;
570 self.exhaust(self.imap(func, iterable));
571 }
572 },
573
574 /** @id MochiKit.Iter.every */
575 every: function (iterable, func) {
576 var self = MochiKit.Iter;
577 try {
578 self.ifilterfalse(func, iterable).next();
579 return false;
580 } catch (e) {
581 if (e != self.StopIteration) {
582 throw e;
583 }
584 return true;
585 }
586 },
587
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;
593 }
594 rval.sort(cmp);
595 return rval;
596 },
597
598 /** @id MochiKit.Iter.reversed */
599 reversed: function (iterable) {
600 var rval = MochiKit.Iter.list(iterable);
601 rval.reverse();
602 return rval;
603 },
604
605 /** @id MochiKit.Iter.some */
606 some: function (iterable, func) {
607 var self = MochiKit.Iter;
608 try {
609 self.ifilter(func, iterable).next();
610 return true;
611 } catch (e) {
612 if (e != self.StopIteration) {
613 throw e;
614 }
615 return false;
616 }
617 },
618
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]);
625 }
626 } else {
627 var self = MochiKit.Iter;
628 iterable = self.iter(iterable);
629 try {
630 while (true) {
631 lst.push(iterable.next());
632 }
633 } catch (e) {
634 if (e != self.StopIteration) {
635 throw e;
636 }
637 }
638 }
639 return lst;
640 },
641
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;
648 }
649 iterable = self.iter(iterable);
650
651 // shared
652 var pk = undefined;
653 var k = undefined;
654 var v;
655
656 function fetch() {
657 v = iterable.next();
658 k = keyfunc(v);
659 };
660
661 function eat() {
662 var ret = v;
663 v = undefined;
664 return ret;
665 };
666
667 var first = true;
668 var compare = m.compare;
669 return {
670 repr: function () { return "groupby(...)"; },
671 next: function() {
672 // iterator-next
673
674 // iterate until meet next group
675 while (compare(k, pk) === 0) {
676 fetch();
677 if (first) {
678 first = false;
679 break;
680 }
681 }
682 pk = k;
683 return [k, {
684 next: function() {
685 // subiterator-next
686 if (v == undefined) { // Is there something to eat?
687 fetch();
688 }
689 if (compare(k, pk) !== 0) {
690 throw self.StopIteration;
691 }
692 return eat();
693 }
694 }];
695 }
696 };
697 },
698
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;
705 }
706
707 iterable = self.iter(iterable);
708 var result = [];
709 var first = true;
710 var prev_key;
711 var compare = m.compare;
712 while (true) {
713 try {
714 var value = iterable.next();
715 var key = keyfunc(value);
716 } catch (e) {
717 if (e == self.StopIteration) {
718 break;
719 }
720 throw e;
721 }
722 if (first || compare(key, prev_key) !== 0) {
723 var values = [];
724 result.push([key, values]);
725 }
726 values.push(value);
727 first = false;
728 prev_key = key;
729 }
730 return result;
731 },
732
733 /** @id MochiKit.Iter.arrayLikeIter */
734 arrayLikeIter: function (iterable) {
735 var i = 0;
736 return {
737 repr: function () { return "arrayLikeIter(...)"; },
738 toString: MochiKit.Base.forwardCall("repr"),
739 next: function () {
740 if (i >= iterable.length) {
741 throw MochiKit.Iter.StopIteration;
742 }
743 return iterable[i++];
744 }
745 };
746 },
747
748 /** @id MochiKit.Iter.hasIterateNext */
749 hasIterateNext: function (iterable) {
750 return (iterable && typeof(iterable.iterateNext) == "function");
751 },
752
753 /** @id MochiKit.Iter.iterateNextIter */
754 iterateNextIter: function (iterable) {
755 return {
756 repr: function () { return "iterateNextIter(...)"; },
757 toString: MochiKit.Base.forwardCall("repr"),
758 next: function () {
759 var rval = iterable.iterateNext();
760 if (rval === null || rval === undefined) {
761 throw MochiKit.Iter.StopIteration;
762 }
763 return rval;
764 }
765 };
766 }
767 });
768
769
770 MochiKit.Iter.EXPORT_OK = [
771 "iteratorRegistry",
772 "arrayLikeIter",
773 "hasIterateNext",
774 "iterateNextIter",
775 ];
776
777 MochiKit.Iter.EXPORT = [
778 "StopIteration",
779 "registerIteratorFactory",
780 "iter",
781 "count",
782 "cycle",
783 "repeat",
784 "next",
785 "izip",
786 "ifilter",
787 "ifilterfalse",
788 "islice",
789 "imap",
790 "applymap",
791 "chain",
792 "takewhile",
793 "dropwhile",
794 "tee",
795 "list",
796 "reduce",
797 "range",
798 "sum",
799 "exhaust",
800 "forEach",
801 "every",
802 "sorted",
803 "reversed",
804 "some",
805 "iextend",
806 "groupby",
807 "groupby_as_array"
808 ];
809
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;
815 } else {
816 /** @id MochiKit.Iter.StopIteration */
817 this.StopIteration = new m.NamedError("StopIteration");
818 }
819 this.iteratorRegistry = new m.AdapterRegistry();
820 // Register the iterator factory for arrays
821 this.registerIteratorFactory(
822 "arrayLike",
823 m.isArrayLike,
824 this.arrayLikeIter
825 );
826
827 this.registerIteratorFactory(
828 "iterateNext",
829 this.hasIterateNext,
830 this.iterateNextIter
831 );
832
833 this.EXPORT_TAGS = {
834 ":common": this.EXPORT,
835 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
836 };
837
838 m.nameFunctions(this);
839
840 };
841
842 MochiKit.Iter.__new__();
843
844 //
845 // XXX: Internet Explorer blows
846 //
847 if (MochiKit.__export__) {
848 reduce = MochiKit.Iter.reduce;
849 }
850
851 MochiKit.Base._exportSymbols(this, MochiKit.Iter);