Issue #18136 has been updated by duerst (Martin D=FCrst).


zverok (Victor Shepelev) wrote:

> **Example 1:** Take pages from paginated API, the last page will have les=
s items than the rest (and that's how we know it is the last):
> =

> ```ruby
> (0..).lazy
>   .map { |offset| get_page(offset, limit) }
>   .take_while_after { |response| response.count =3D=3D limit } # the last=
 will have, say, 10 items, but should still be included!
>   .map { process response somehow }
> ```

Couldn't this be written with `.take_while { |response| response.count > 0 =
}`


> **Example 2:** Same as above, but "we should continue pagination" is spec=
ified with a separate data key "can_continue":
> ```ruby
> (0..).lazy
>   .map { |offset| get_page(offset, limit) }
>   .take_while_after { |response| response['can_continue'] } # the last wi=
ll have can_continue=3Dfalse, but still has data
>   .map { process response somehow }
> ```

The problem may also be that the overall interface doesn't seem to be desig=
ned very well. Marking the last piece of data as special looks wrong; intro=
ducing a next object that is marked as not being part of data seems much mo=
re appropriate.

Also, a higher-level starting interface (one that produces pages rather tha=
n have to start with a series of integers) seems more appropriate.

----------------------------------------
Feature #18136: take_while_after
https://bugs.ruby-lang.org/issues/18136#change-93500

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
----------------------------------------
Sorry, I already tried that once (#16441) but I failed to produce the persu=
asive example.
So I am back with a couple of them, much simpler and clear than my initial.

**The proposal itself:** Have `take_while_after` which behaves like `take_w=
hile` but also includes the last element (first where the condition failed)=
. Reason: there are a lot of cases where "the last good item" in enumeratio=
n is the distinctive one (one where enumeration should stop, but the item i=
s still good.

**Example 1:** Take pages from paginated API, the last page will have less =
items than the rest (and that's how we know it is the last):

```ruby
(0..).lazy
  .map { |offset| get_page(offset, limit) }
  .take_while_after { |response| response.count =3D=3D limit } # the last w=
ill have, say, 10 items, but should still be included!
  .map { process response somehow }
```

**Example 2:** Same as above, but "we should continue pagination" is specif=
ied with a separate data key "can_continue":
```ruby
(0..).lazy
  .map { |offset| get_page(offset, limit) }
  .take_while_after { |response| response['can_continue'] } # the last will=
 have can_continue=3Dfalse, but still has data
  .map { process response somehow }
```

**Exampe 3:** Taking a sentence from a list of tokens like this:
```ruby
tokens =3D [
  {text: 'Ruby', type: :word},
  {text: 'is', type: :word},
  {text: 'cool', type: :word},
  {text: '.', type: :punctuation, ends_sentence: true},
  {text: 'Rust', type: :word},
  # ...
]

sentence =3D tokens.take_while_after { _1[:ends_sentence] }
```

(I can get more if it is necessary!)

Neither of those can be solved by "Using `take_while` with proper condition=
.", as @matz suggested here: https://bugs.ruby-lang.org/issues/16441#note-9

I typically solve it by `slice_after { condition }.first`, but that's a) ug=
lier and b) greedy when we are working with lazy enumerator (so for API exa=
mples, all paginated pages would be fetched at once, and only then processe=
d).

Another consideration in #16441 was an unfortunate naming.
I am leaving it to discussion, though I tend to like `#take_upto` from #164=
46.



-- =

https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=3Dunsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>