add legend
[sgn.git] / js / MochiKit / Iter.js
blobc2fcbeee05b462288c7010c1d8f6bf9a0695a8e1
1 /***
3 MochiKit.Iter 1.4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
7 (c) 2005 Bob Ippolito.  All rights Reserved.
9 ***/
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", []);
20 try {
21     if (typeof(MochiKit.Base) == 'undefined') {
22         throw "";
23     }
24 } catch (e) {
25     throw "MochiKit.Iter depends on MochiKit.Base!";
28 if (typeof(MochiKit.Iter) == 'undefined') {
29     MochiKit.Iter = {};
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     },
42     /** @id MochiKit.Iter.registerIteratorFactory  */
43     registerIteratorFactory: function (name, check, iterfactory, /* optional */ override) {
44         MochiKit.Iter.iteratorRegistry.register(name, check, iterfactory, override);
45     },
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         }
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     },
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     },
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     },
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     },
158     /** @id MochiKit.Iter.next */
159     next: function (iterator) {
160         return iterator.next();
161     },
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     },
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     },
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     },
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     },
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     },
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     },
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     },
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     },
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     },
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];
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     },
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     },
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         }
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     },
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     },
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     },
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     },
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     },
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     },
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     },
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     },
598     /** @id MochiKit.Iter.reversed */
599     reversed: function (iterable) {
600         var rval = MochiKit.Iter.list(iterable);
601         rval.reverse();
602         return rval;
603     },
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     },
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     },
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);
651         // shared
652         var pk = undefined;
653         var k = undefined;
654         var v;
656         function fetch() {
657             v = iterable.next();
658             k = keyfunc(v);
659         };
661         function eat() {
662             var ret = v;
663             v = undefined;
664             return ret;
665         };
667         var first = true;
668         var compare = m.compare;
669         return {
670             repr: function () { return "groupby(...)"; },
671             next: function() {
672                 // iterator-next
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     },
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         }
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     },
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     },
748     /** @id MochiKit.Iter.hasIterateNext */
749     hasIterateNext: function (iterable) {
750         return (iterable && typeof(iterable.iterateNext) == "function");
751     },
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     }
770 MochiKit.Iter.EXPORT_OK = [
771     "iteratorRegistry",
772     "arrayLikeIter",
773     "hasIterateNext",
774     "iterateNextIter",
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"
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     );
827     this.registerIteratorFactory(
828         "iterateNext",
829         this.hasIterateNext,
830         this.iterateNextIter
831     );
833     this.EXPORT_TAGS = {
834         ":common": this.EXPORT,
835         ":all": m.concat(this.EXPORT, this.EXPORT_OK)
836     };
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);