[ruby-core:105091] [Ruby master Feature#18136] take_while_after
From:
duerst <noreply@...>
Date:
2021-08-30 07:11:31 UTC
List:
ruby-core #105091
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:[email protected]?subject=3Dunsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>