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
Updated by duerst (Martin Dürst) 1 day ago
My impression is that this proposal is useful in some cases, but these are too rare to include it in core. This functionality is better suited for a gem. Maybe it's already available in some gem.
Updated by datibbaw (Tjerk Meesters) about 22 hours ago
duerst (Martin Dürst) wrote in #note-1:
My impression is that this proposal is useful in some cases, but these are too rare to include it in core. This functionality is better suited for a gem. Maybe it's already available in some gem.
I did search around for gems that come close; rubyworks/facets has Enumerable#clusters which is similar to slice_when and returns an array of arrays, or Array#span which is just a wrapper over find_index and take/drop.
It's certainly possible to pour this idea into a gem, along the lines of operations that return "pointers" into an array rather than a full copy.