Project

General

Profile

Actions

Feature #22536

open

Add Enumerable#longest_slice to return the range of the longest consecutive matching sequence

Feature #22536: Add Enumerable#longest_slice to return the range of the longest consecutive matching sequence

Added by datibbaw (Tjerk Meesters) 2 days ago. Updated about 22 hours ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:127414]

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...0 for 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 each to 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 Actions #1 [ruby-core:127432]

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 Actions #2 [ruby-core:127444]

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.

Actions

Also available in: PDF Atom