Project

General

Profile

Feature #16441

Updated by sawa (Tsuyoshi Sawada) almost 5 years ago

The method is just like `#take_while`, but also includes the item where condition became true. 

 Examples of usefulness: 

 
 ```ruby 
 str = <<DOC 
 prologue 
 << 
 1 
 2 
 3 
 >> 
 epilogue 
 DOC 
 ``` 

 # Imagine we want to take everything starting from `<<` << to `>>` >> in short and clean Ruby. Ruby 
 # Surprisingly, our best guess would be infamous flip-flop: 

 ```ruby 
 p str.each_line(chomp: true).filter_map { _1 if _1 == '<<'.._1 == '>>' } 
 # => ["<<", "1", "2", "3", ">>"] 
 ``` 

 # Trying to achieve this with `Enumerator`, Enumerator, you _almost_ can express it, but the last line is lost: 

 ```ruby but... 
 p str.each_line(chomp: true).drop_while { _1 != '<<' }.take_while { _1 != '>>' } 
 # => ["<<", "1", "2", "3"] 
 ``` -- the last line is lost. 

 # So, Enumerable leaves us with this (which is harder to read, due to additional `.first`): 

 ```ruby 
 p str.each_line(chomp: true).drop_while { _1 != '<<' }.slice_after { _1 == '>>' }.first 
 # => ["<<", "1", "2", "3", ">>"] 
 ``` 

 # With proposed method: 

 ```ruby 
 p str.each_line(chomp: true).drop_while { _1 != '<<' }.take_while_after { _1 != '>>' } 
 # => ["<<", "1", "2", "3", ">>"] 
 ``` 

 The idea is the same as with flip-flops `..` vs `...` (sometimes we need to include the last element matching the condition, sometimes don't), and `while ... end` vs `do ... while`. Another example (from `Enumerator.produce` [proposal](https://bugs.ruby-lang.org/issues/14781)): 

 ```ruby 
 require 'strscan' 
 scanner = StringScanner.new('7+38/6') 

 
 p Enumerator.produce { scanner.scan(%r{\d+|[-+*/]}) }.take_while { !scanner.eos? } # expresses meaning, but loses last element 
 # => ["7", "+", "38", "/"] 

 
 p Enumerator.generate { scanner.scan(%r{\d+|[-+*/]}) }.slice_after { scanner.eos? }.first # slice_after {}.first again 
 # => ["7", "+", "38", "/", "6"] 

 
 p Enumerator.produce { scanner.scan(%r{\d+|[-+*/]}) }.take_while_after { !scanner.eos? } 
 # => ["7", "+", "38", "/", "6"] 
 ``` 

 PS: Not sure about the name, suggestions are welcome

Back