Issue #16446 has been updated by Dan0042 (Daniel DeLorme).


I think the positive counterparts of `take_while/drop_while` should be `take_until/drop_until`. And although `take_upto` and `drop_upto` are nicely intuitive and descriptive, I can't think of any good names for their negative counterparts (3) and (4). That may be an indication that we'd be better with a parameter as wishdev suggests. However I find that `:left` and `:right` do not read naturally at all. Maybe a number to indicate how many extra items to take or drop:

```ruby
enum.take_while(&:nonzero?) # => [1, 1]
enum.drop_while(&:nonzero?) # => [0, 3, 3, 0, 5, 5]
enum.take_while(+1, &:nonzero?) # => [1, 1, 0]
enum.drop_while(+1, &:nonzero?) # => [3, 3, 0, 5, 5]
```

It's worth noting that there's several other succint ways to accomplish the same thing, so I'm not convinced it's worth adding that many methods. Not everything has to be a one-liner.

```ruby
i = ary.index(&:zero?) || ary.size
ary[0...i]     # => [1, 1]
ary[0..i]      # => [1, 1, 0]
ary[i..-1]     # => [0, 3, 3, 0, 5, 5]
ary[i+1..-1]   # => [3, 3, 0, 5, 5]

n = enum.find_index(&:zero?) || enum.count
enum.take(n)   # => [1, 1]
enum.take(n+1) # => [1, 1, 0]
enum.drop(n)   # => [0, 3, 3, 0, 5, 5]
enum.drop(n+1) # => [3, 3, 0, 5, 5]
```

---

BTW you could also classify the three factors as:

(1) Whether we want to express the items to include (take) or exclude (drop)
(2) Whether we want the left side or the right side in the returned output
(3) Whether we want the items to be inclusive or exclusive of the boundary element

| |(1)|(2)|(3)|method|example|
|--|--|--|--|--|--|
|1|take|left |exclusive|take_before|enum.take_before(&:zero?) # => [1, 1]
|1|take|left |inclusive|take_upto  |enum.take_upto(&:zero?)   # => [1, 1, 0]
|1|take|right|inclusive|take_from  |enum.take_from(&:zero?)   # => [0, 3, 3, 0, 5, 5]
|1|take|right|exclusive|take_after |enum.take_after(&:zero?)  # => [3, 3, 0, 5, 5]
|1|drop|left |exclusive|drop_before|enum.drop_before(&:zero?) # => [0, 3, 3, 0, 5, 5]
|1|drop|left |inclusive|drop_upto  |enum.drop_upto(&:zero?)   # => [3, 3, 0, 5, 5]
|1|drop|right|inclusive|drop_from  |enum.drop_from(&:zero?)   # => [1, 1]
|1|drop|right|exclusive|drop_after |enum.drop_after(&:zero?)  # => [1, 1, 0]

This is what I'd call a declarative style, as it describes *what* the method does, not *how*. `take_while` feels more like an imperative style to me since it describes the *how*, the control flow.

Ultimately there's so many different ways to think about and express any given operation... there's More Than One Way To Do It but they don't all have to be in the ruby core.

----------------------------------------
Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
https://bugs.ruby-lang.org/issues/16446#change-83457

* Author: sawa (Tsuyoshi Sawada)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
#16441 led me to think about the issue more generally. When we want to split a series of iterations by the first element that satisfies (or dissatisfies) a condition, we have three factors to consider.

(1) Whether we want the condition to work **negatively** or **positively**
(2) Whether we want the first element to satisfy (or dissatisfy) the condition to be included in the **left** side or the **right** side of the split
(3) Whether we want the **left** side or the **right** side in the returned output

This leads us to eight possible combinations to consider.

```ruby
enum = [1, 1, 0, 3, 3, 0, 5, 5].to_enum
```

| |(1)|(2)|(3)|method|example|
|--|--|--|--|--|--|
|1|negatively|left|left|`take_while`|`enum.foo1(&:nonzero?) # => [1, 1]`|
|2|negatively|left|right|`drop_while`|`enum.foo2(&:nonzero?) # => [0, 3, 3, 0, 5, 5]`|
|3|negatively|right|left||`enum.foo3(&:nonzero?) # => [1, 1, 0]`|
|4|negatively|right|right||`enum.foo4(&:nonzero?) # => [3, 3, 0, 5, 5]`|
|5|positively|left|left||`enum.foo5(&:zero?) # => [1, 1]`|
|6|positively|left|right||`enum.foo6(&:zero?) # => [0, 3, 3, 0, 5, 5]`|
|7|positively|right|left||`enum.foo7(&:zero?) # => [1, 1, 0]`|
|8|positively|right|right||`enum.foo8(&:zero?) # => [3, 3, 0, 5, 5]`|

Proposal #16441 asks for a method that corresponds to case 3 in the table above, but I think that would make the paradigm messy unless case 4 is also implemented. Either cases 3 and 4 should both be implemented, or both not. Actually, the current proposal is not about cases 3 and 4. I would leave that to #16641.

In many use cases (including the first example in #16641), we want to detect the "marker element" by which we split the iterations. In the cases above, that can be the element `0`. In such use cases, it is more natural to describe the condition in positive terms (i.e., `zero?`) rather than negative terms (i.e., `nonzero?`). (And in other use cases, it might be the other way around.) So I would like to propose methods that correspond to cases 5, 6, 7, 8 above.

Naming of the methods should be done systematically. As a candidate, I came up with the following:

||method|
|--|--|
|5|`take_before`|
|6|`drop_before`|
|7|`take_upto`|
|8|`drop_upto`|




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

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