<div id="__MailbirdStyleContent" style="font-size: 10pt;font-family: Arial;color: #000000;text-align: left" dir="ltr"><div><span style="font-size: 13.3333px">Hi all! :-)</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">Here is an interesting issue that I repeatedly come across when it comes to enumerating a (sequenceable/ordered) collection up to a certain element, while collecting (i.e. mapping) new elements from that collection. It seems rather tedious to express in code, meaning more than 2-3 lines and maybe involving even temporary variables.</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">Our stream protocol has #upTo:, which works when you know about the specific element you are looking for.<b> It does not work, however, when you just have an indirect query that should match.</b></span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">Here is an example. From a list of integer numbers, extract all even ones up to (including) 50, then square those numbers.</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">(1 to: 100)</span></div><div><span style="font-size: 13.3333px">   select: [:num | <b>num <= 50</b> and: [num even]]</span></div><div><span style="font-size: 13.3333px">   thenCollect: [:num | num * num].</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">A stream-based approach to shorten the predicate using #upTo: could look like this:</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">((1 to: 100) readStream upTo: 50)</span></div><div><span style="font-size: 13.3333px">   select: [:num | num even]</span></div><div><span style="font-size: 13.3333px">   thenCollect: [:num | num * num].</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">Now imagine that, unfortunately, we don't know about "50" but only a query, say "a number that is squared 2500". Since #upTo: cannot do that and I do not want to jump collection-stream-collection for this issue, the non-stream approach could look like this:</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">(1 to: 100)</span></div><div><span style="font-size: 13.3333px">   select: [:num | <b>(num * num <= 2500)</b> and: [num even]]</span></div><div><span style="font-size: 13.3333px">   thenCollect: [:num | num * num].</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">Yet, this is still not my actual issue. What if that collection is not ordered at all but only has a stable sequence? Then we would need to mark the specific element and then reject every element that comes after that. (Note that we replaced #<= with #= to indicate "sequence" instead of "order".)</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">| found |</span></div><div><span style="font-size: 13.3333px">found := false.</span></div><div><span style="font-size: 13.3333px">(1 to: 100)</span></div><div><span style="font-size: 13.3333px">   select: [:num |</span></div><div><span style="font-size: 13.3333px">      found := found or: [<b>num * num = 2500</b>].</span></div><div><span style="font-size: 13.3333px">      found not and: [num even]]</span></div><div><span style="font-size: 13.3333px">   thenCollect: [:num | num * num].</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">Uh oh! We just skipped the stop element itself. What if we want to include it? Maybe like this:</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">| found |</span></div><div><span style="font-size: 13.3333px">found := false.</span></div><div><span style="font-size: 13.3333px">(1 to: 100)</span></div><div><span style="font-size: 13.3333px">   select: [:num | | firstMatch |</span></div><div><span style="font-size: 13.3333px">      firstMatch := found not and: [<b>num * num = 2500</b>].</span></div><div><span style="font-size: 13.3333px">      found := found | firstMatch.</span></div><div><span style="font-size: 13.3333px">      found not | firstMatch and: [num even]]</span></div><div><span style="font-size: 13.3333px">   thenCollect: [:num | num * num].</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">Now this looks like a rather complicated implementation for a simple query. Let me give you a last approach using the infamous #valueWithExit.</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">| list stop select collect result |</span></div><div><span style="font-size: 13.3333px">list := 1 to: 100.</span></div><div><span style="font-size: 13.3333px"><b>stop := [:num | num * num = 2500].</b></span></div><div><span style="font-size: 13.3333px">select := [:num | num even].</span></div><div><span style="font-size: 13.3333px">collect := [:num | num * num].</span></div><div><span style="font-size: 13.3333px">result := OrderedCollection new.</span></div><div><span style="font-size: 13.3333px">[:break |</span></div><div><span style="font-size: 13.3333px">   list do: [:num |</span></div><div><span style="font-size: 13.3333px">      (select value: num) ifTrue: [result add: (collect value: num)].</span></div><div><span style="font-size: 13.3333px">      (<b>stop value: num</b>) ifTrue: [break value]].</span></div><div><span style="font-size: 13.3333px">] valueWithExit.</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">Phew. Is there a simpler approach to this problem? I am looking for something like #collect:until: or #upToSatisfying:. I do want the stop element to be included here, not sure about the general case.</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px"><b>aButton withAllOwners</b></span></div><div><span style="font-size: 13.3333px"><b>   collect: [:morph | morph color]</b></span></div><div><span style="font-size: 13.3333px"><b>   until: [:morph | morph isSystemWindow].</b></span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">(aButton withAllOwners readStream</span></div><div><span style="font-size: 13.3333px">   upToSatisfying: [:morph | isSystemWindow])</span></div><div><span style="font-size: 13.3333px">   collect: [:morph | morph color].</span></div><div><span style="font-size: 13.3333px"><br></span></div><div><span style="font-size: 13.3333px">Best,</span></div><div><span style="font-size: 13.3333px">Marcel</span></div><div class="mb_sig"></div></div>