deprecate SCViewHolder-layRight
[supercollider.git] / SCClassLibrary / Common / Collections / Collection.sc
blobdc3962304ecb3b777bfbace8f62dd33e9cb6fceb
1 Collection {
2         *newFrom { | aCollection |
3                 var newCollection = this.new(aCollection.size);
4                 aCollection.do {| item | newCollection.add(item) };
5                 ^newCollection
6         }
7         *with { | ... args |
8                 var newColl;
9                 // answer a collection of my class of the given arguments
10                 // the class Array has a simpler implementation
11                 newColl = this.new(args.size);
12                 newColl.addAll(args);
13                 ^newColl
14         }
15         *fill { | size, function |
16                 var obj;
17                 obj = this.new(size);
18                 size.do { | i |
19                         obj.add(function.value(i));
20                 };
21                 ^obj
22         }
24         @ { | index | ^this[index] }
26         == { | aCollection |
27                 if (aCollection.class != this.class) { ^false };
28                 if (this.size != aCollection.size) { ^false };
29                 this.do { | item, i |
30                         // this is enough since both collections are finite
31                         if ((aCollection.includes(item)).not) { ^false };
32                 };
33                 ^true
34         }
35         hash {
36                 var hash = this.class.hash;
37                 this.do { | item |
38                         hash = hash bitXor: item.hash;
39                 };
40                 ^hash
41         }
43         species { ^Array }
44         do { ^this.subclassResponsibility(thisMethod) }
45         iter {
46                 ^r { this.do {|item| item.yield } }
47         }
48         size {
49                 // this is the slow way. Most collections have a faster way.
50                 var tally = 0;
51                 this.do { tally = tally + 1 };
52                 ^tally
53         }
54         flatSize {
55                 ^this.sum(_.flatSize)
56         }
58         isEmpty { ^this.size == 0 }
59         notEmpty { ^this.size > 0 }
60         asCollection { ^this }
62         add { ^this.subclassResponsibility(thisMethod) }
63         addAll { | aCollection | aCollection.asCollection.do { | item | this.add(item) } }
64         remove { ^this.subclassResponsibility(thisMethod) }
65         removeAll { | list | list.do { | item | this.remove(item) } }
66         removeEvery { | list | this.removeAllSuchThat(list.includes(_)) }
67         removeAllSuchThat { | function |
68                 var removedItems = this.class.new;
69                 var copy = this.copy;
70                 copy.do { | item, i |
71                         if ( function.value(item, i) )
72                         {
73                                 this.remove(item);
74                                 removedItems = removedItems.add(item);
75                         }
76                 };
77                 ^removedItems
78         }
80         atAll { arg keys;
81                 ^keys.collect {|index| this[index] }
82         }
83         putEach { arg keys, values;
84                 // works for ArrayedCollections and Dictionaries
85                 keys = keys.asArray;
86                 values = values.asArray;
87                 keys.do { |key, i| this[key] = values.wrapAt(i) } ;
88         }
90         includes { | item1 |
91                 this.do {|item2| if (item1 === item2) {^true} };
92                 ^false
93         }
94         includesEqual { | item1 |
95                 this.do {|item2| if (item1 == item2) {^true} };
96                 ^false
97         }
98         includesAny { | aCollection |
99                 aCollection.do { | item | if (this.includes(item)) {^true} };
100                 ^false
101         }
102         includesAll { | aCollection |
103                 aCollection.do { | item | if (this.includes(item).not) {^false} };
104                 ^true
105         }
106         matchItem { | item |
107                 ^this.includes(item)
108         }
110         collect { | function |
111                 ^this.collectAs(function, this.species);
112         }
113         select { | function |
114                 ^this.selectAs(function, this.species);
115         }
116         reject { | function |
117                 ^this.rejectAs(function, this.species);
118         }
119         collectAs { | function, class |
120                 var res = class.new(this.size);
121                 this.do {|elem, i| res.add(function.value(elem, i)) }
122                 ^res;
123         }
124         selectAs { | function, class |
125                 var res = class.new(this.size);
126                 this.do {|elem, i| if (function.value(elem, i)) { res = res.add(elem) } }
127                 ^res;
128         }
129         rejectAs { | function, class |
130                 var res = class.new(this.size);
131                 this.do {|elem, i|
132                         if (function.value(elem, i).not) {res.add(elem)}
133                 }
134                 ^res;
135         }
136         detect { | function |
137                 this.do {|elem, i| if (function.value(elem, i)) { ^elem } }
138                 ^nil;
139         }
140         detectIndex { | function |
141                 this.do {|elem, i| if (function.value(elem, i)) { ^i } }
142                 ^nil;
143         }
144         doMsg { | selector ... args |
145                 this.do {| item | item.performList(selector, args) }
146         }
147         collectMsg { | selector ... args |
148                 ^this.collect {| item | item.performList(selector, args) }
149         }
150         selectMsg { | selector ... args |
151                 ^this.select {| item | item.performList(selector, args) }
152         }
153         rejectMsg { | selector ... args |
154                 ^this.reject {| item | item.performList(selector, args) }
155         }
156         detectMsg { | selector ... args |
157                 ^this.detect {| item | item.performList(selector, args) }
158         }
159         detectIndexMsg { | selector ... args |
160                 ^this.detectIndex {| item | item.performList(selector, args) }
161         }
162         lastForWhich { | function |
163                 var prev;
164                 this.do {|elem, i|
165                         if (function.value(elem, i)) {
166                                 prev = elem;
167                         }{
168                                 ^prev
169                         }
170                 };
171                 ^prev
172         }
173         lastIndexForWhich { | function |
174                 var prev;
175                 this.do {|elem, i|
176                         if (function.value(elem, i)) {
177                                 prev = i;
178                         }{
179                                 ^prev
180                         }
181                 };
182                 ^prev
183         }
185         inject { | thisValue, function |
186                 var nextValue = thisValue;
187                 this.do { | item, i |
188                         nextValue = function.value(nextValue, item, i);
189                 };
190                 ^nextValue
191         }
192         count { | function |
193                 var sum = 0;
194                 this.do {|elem, i| if (function.value(elem, i)) { sum=sum+1 } }
195                 ^sum;
196         }
197         occurrencesOf { | obj |
198                 var sum = 0;
199                 this.do { | elem | if (elem == obj) { sum=sum+1 } }
200                 ^sum;
201         }
202         any { | function |
203                 this.do {|elem, i| if (function.value(elem, i)) { ^true } }
204                 ^false;
205         }
206         every { | function |
207                 this.do {|elem, i| if (function.value(elem, i).not) { ^false } }
208                 ^true;
209         }
210         sum { | function |
211                 var sum = 0;
212                 if (function.isNil) { // optimized version if no function
213                         this.do { | elem | sum = sum + elem; }
214                 }{
215                         this.do {|elem, i| sum = sum + function.value(elem, i); }
216                 }
217                 ^sum;
218         }
219         mean { | function |
220                 ^this.sum(function) / this.size;
221         }
222         product { | function |
223                 var product = 1;
224                 if (function.isNil) { // optimized version if no function
225                         this.do { | elem | product = product * elem; }
226                 }{
227                         this.do {|elem, i| product = product * function.value(elem, i); }
228                 }
229                 ^product;
230         }
231         sumabs {  // sum of the absolute values - used to convert Mikael Laursen's rhythm lists.
232                 var sum = 0;
233                 this.do { | elem |
234                         if (elem.isSequenceableCollection) { elem = elem[0] };
235                         sum = sum + elem.abs;
236                 }
237                 ^sum;
238         }
240         maxItem { | function |
241                 var maxValue, maxElement;
242                 if (function.isNil) { // optimized version if no function
243                         this.do { | elem |
244                                 if (maxElement.isNil) {
245                                         maxElement = elem;
246                                 }{
247                                         if (elem > maxElement) {
248                                                 maxElement = elem;
249                                         }
250                                 }
251                         }
252                         ^maxElement;
253                 }{
254                         this.do {|elem, i| var val;
255                                 if (maxValue.isNil) {
256                                         maxValue = function.value(elem, i);
257                                         maxElement = elem;
258                                 }{
259                                         val = function.value(elem, i);
260                                         if (val > maxValue) {
261                                                 maxValue = val;
262                                                 maxElement = elem;
263                                         }
264                                 }
265                         }
266                         ^maxElement;
267                 }
268         }
269         minItem { | function |
270                 var minValue, minElement;
271                 if (function.isNil) { // optimized version if no function
272                         this.do {|elem, i|
273                                 if (minElement.isNil) {
274                                         minElement = elem;
275                                 }{
276                                         if (elem < minElement) {
277                                                 minElement = elem;
278                                         }
279                                 }
280                         };
281                         ^minElement;
282                 }{
283                         this.do {|elem, i| var val;
284                                 if (minValue.isNil) {
285                                         minValue = function.value(elem, i);
286                                         minElement = elem;
287                                 }{
288                                         val = function.value(elem, i);
289                                         if (val < minValue) {
290                                                 minValue = val;
291                                                 minElement = elem;
292                                         }
293                                 }
294                         }
295                         ^minElement;
296                 }
297         }
299         maxIndex { | function |
300                 var maxValue, maxIndex;
301                 if (function.isNil) { // optimized version if no function
302                         this.do { | elem, index |
303                                 if (maxValue.isNil) {
304                                         maxValue = elem;
305                                         maxIndex = index;
306                                 }{
307                                         if (elem > maxValue) {
308                                                 maxValue = elem;
309                                                 maxIndex = index;
310                                         }
311                                 }
312                         }
313                         ^maxIndex;
314                 }{
315                         this.do {|elem, i| var val;
316                                 if (maxValue.isNil) {
317                                         maxValue = function.value(elem, i);
318                                         maxIndex = i;
319                                 }{
320                                         val = function.value(elem, i);
321                                         if (val > maxValue) {
322                                                 maxValue = val;
323                                                 maxIndex = i;
324                                         }
325                                 }
326                         }
327                         ^maxIndex;
328                 }
329         }
331         minIndex { | function |
332                 var minValue, minIndex;
333                 if (function.isNil) { // optimized version if no function
334                         this.do {|elem, i|
335                                 if (minValue.isNil) {
336                                         minValue = elem;
337                                         minIndex = i;
338                                 }{
339                                         if (elem < minValue) {
340                                                 minValue = elem;
341                                                 minIndex = i;
342                                         }
343                                 }
344                         };
345                         ^minIndex;
346                 }{
347                         this.do {|elem, i| var val;
348                                 if (minValue.isNil) {
349                                         minValue = function.value(elem, i);
350                                         minIndex = i;
351                                 }{
352                                         val = function.value(elem, i);
353                                         if (val < minValue) {
354                                                 minValue = val;
355                                                 minIndex = i;
356                                         }
357                                 }
358                         }
359                         ^minIndex;
360                 }
361         }
364         maxValue { | function |                 // must supply a function
365                 var maxValue, maxElement;
366                 this.do {|elem, i|
367                                 var val;
368                                 if (maxValue.isNil) {
369                                         maxValue = function.value(elem, i);
370                                         maxElement = elem;
371                                 }{
372                                         val = function.value(elem, i);
373                                         if (val > maxValue) {
374                                                 maxValue = val;
375                                                 maxElement = elem;
376                                         }
377                                 }
378                 };
379                 ^maxValue;
380         }
381         minValue { | function |
382                 var minValue, minElement;
383                 this.do {|elem, i|
384                                 var val;
385                                 if (minValue.isNil) {
386                                         minValue = function.value(elem, i);
387                                         minElement = elem;
388                                 }{
389                                         val = function.value(elem, i);
390                                         if (val < minValue) {
391                                                 minValue = val;
392                                                 minElement = elem;
393                                         }
394                                 }
395                 };
396                 ^minValue;
397         }
399         invert { | axis |
400                 var index;
401                 // can be used to invert a pitch list about a given axis
402                 // [3, 2, 9, 7].invert(11) becomes [ 19, 20, 13, 15 ]
403                 // if axis is nil, invert uses the registral center
404                 // [3, 2, 9, 7].invert becomes [ 8, 9, 2, 4 ]
405                 if(this.isEmpty) { ^this.species.new };
406                 if(axis.notNil) { index = axis * 2 } { index = this.minItem + this.maxItem };
407                 ^index - this;
408         }
410         sect { | that |
411                 var result = this.species.new;
412                 this.do { | item |
413                         if (that.includes(item)) {
414                                 result = result.add(item);
415                         }
416                 };
417                 ^result
418         }
419         union { | that |
420                 var result = this.copy;
421                 that.do { | item |
422                         if (result.includes(item).not) {
423                                 result = result.add(item);
424                         }
425                 };
426                 ^result
427         }
428         difference { | that |
429                 ^this.copy.removeAll(that);
430         }
431         symmetricDifference { | that |
432                 var result = this.species.new;
433                 this.do { | item |
434                         if (that.includes(item).not) {
435                                 result = result.add(item);
436                         }
437                 };
438                 that.do { | item |
439                         if (this.includes(item).not) {
440                                 result = result.add(item);
441                         }
442                 };
443                 ^result;
444         }
445         isSubsetOf { | that | ^that.includesAll(this) }
447         asArray { ^Array.new(this.size).addAll(this); }
448         asBag { ^Bag.new(this.size).addAll(this); }
449         asList { ^List.new(this.size).addAll(this); }
450         asSet { ^Set.new(this.size).addAll(this); }
451         asSortedList { | function | ^SortedList.new(this.size, function).addAll(this); }
453         powerset {
454                 var species = this.species;
455                 var result = this.asArray.powerset;
456                 ^if(species == Array) { result } {
457                         result.collectAs({ | item | item.as(species) }, species)
458                 }
459         }
461         flopDict { | unbubble=true |
462                 var res, first = true;
463                 this.do { | dict |
464                         if(first) { res = dict.class.new; first = false };
465                         dict.keysValuesDo { | key, val |
466                                 res[key] = res[key].add(val)
467                         }
468                 };
469                 if(unbubble) { res = res.collect(_.unbubble) };
470                 ^res
471         }
473         histo { arg steps = 100, min, max;
474                 var freqs, freqIndex, lastIndex, stepSize, outliers = 0;
475                 if(this.isEmpty) { ^this.species.new };
476                 min = min ?? { this.minItem };
477                 max = max ?? { this.maxItem };
479                 freqs = Array.fill(steps, 0);
480                 lastIndex = steps - 1;
481                 stepSize = steps / (max - min);
483                 this.do { arg el;
484                         freqIndex = ((el - min) * stepSize).asInteger;
486                         if (freqIndex.inclusivelyBetween(0, lastIndex)) {
487                                 freqs[freqIndex] = freqs[freqIndex] + 1;
488                         } {
489                                                 // if max is derived from maxItem, count it in:
490                                 if (el == max) {
491                                         freqs[steps-1] = freqs[steps-1] + 1;
492                                 } {             // else it is an outlier.
493                                         outliers =  outliers + 1;
494                                 };
495                         };
496                 };
498                 if (outliers > 0) {
499                         ("histogram :" + outliers + "out of (histogram) range values in collection.").inform;
500                 };
502                 ^freqs;
503         }
505 //      printAll { this.do { | item | item.postln; }; } // convenience method
506         printAll { |before, after|
507                 if (before.isNil and: after.isNil) {
508                         this.do { | item | item.postln; };
509                 } {
510                         before = before ? ""; after = after ? "";
511                         this.do { | item | before.post; item.post; after.postln; };
512                 };
513         }
515         printcsAll { this.do { | item | item.postcs; }; } // convenience method
516         dumpAll { this.do { | item | item.dump; }; } // convenience method
518         printOn { | stream |
519                 if (stream.atLimit) { ^this };
520                 stream << this.class.name << "[ " ;
521                 this.printItemsOn(stream);
522                 stream << " ]" ;
523         }
524         storeOn { | stream |
525                 if (stream.atLimit) { ^this };
526                 stream << this.class.name << "[ " ;
527                 this.storeItemsOn(stream);
528                 stream << " ]" ;
529         }
530         storeItemsOn { | stream |
531                 var addComma = false;
532                 this.do { | item |
533                         if (stream.atLimit) { ^this };
534                         if (addComma) { stream.comma.space; } { addComma = true };
535                         item.storeOn(stream);
536                 };
537         }
538         printItemsOn { | stream |
539                 var addComma = false;
540                 this.do { | item |
541                         if (stream.atLimit) { ^this };
542                         if (addComma) { stream.comma.space; } { addComma = true };
543                         item.printOn(stream);
544                 };
545         }
547         // Synth support
549         writeDef { | file |
550                 file.putString("SCgf");
551                 file.putInt32(1); // file version
552                 file.putInt16(this.size); // number of defs in file.
554                 this.do { | item | item.writeDef(file); }
555         }
557         writeInputSpec { | file, synthDef |
558                 this.do { | item | item.writeInputSpec(file, synthDef) };
559         }
561         // Flow control
562         case { | default |
563                 var out = this.detect {|it| it.key.value;};
564                 if (out.notNil) {
565                         ^out.value.value
566                 }{
567                         ^default.value;
568                 }
569         }
571         // Event support
572         makeEnvirValPairs {
573                 var res = Array.new(this.size * 2);
574                 this.do { |item|
575                         res.add(item);
576                         res.add(currentEnvironment[item]);
577                 };
578                 ^res
579         }