Feature #22536
openAdd Enumerable#longest_slice to return the range of the longest consecutive matching sequence
Description
I propose adding Enumerable#longest_slice, which returns a Range identifying the longest consecutive sequence of matching entries.
Without a block, entries match when they are truthy. Both nil and false break a sequence. With a block, entries match when the block returns a truthy value.
Motivation¶
Finding the longest consecutive matching sequence currently requires manually tracking positions and run lengths, or creating intermediate collections. Returning a Range makes the result directly usable for slicing an Array or identifying positions in another enumerable.
I ran into this when analysing IPv6 compression logic and reasoned that it may have other uses; in comparison, longest prefix / suffix is already covered with e.g. #index and #rindex using block syntax.
Examples¶
[nil, 'a', 'b', nil, 'c'].longest_slice
# => 1...3
[1, 2, 4, 3, 6].longest_slice(&:even?)
# => 1...3
[false, 0, []].longest_slice
# => 1...3
a = [10, 20, 30, 40]
a.each.with_index(1).longest_slice do |value, index|
value >= 20 && index <= 3
end
# => 1...3
Proposed behavior¶
- Return a zero-based, end-exclusive Range.
- Return
0...0for an empty enumerable or when no entries match. - When multiple matching sequences have the same maximum length, return the first.
- Forward all arguments yielded by
eachto the block, supporting indexed enumerators and multi-argument iterators. - Always express the result in iteration positions, independently of any index yielded to the block.
- Traverse the enumerable once, using constant auxiliary space.
The name longest_slice and the API details are open for discussion.
A draft implementation with documentation and tests is available at:
https://github.com/ruby/ruby/pull/19838