Paul King created GROOVY-12016:
----------------------------------

             Summary: New GDK methods: zipWithNext and groupConsecutive
                 Key: GROOVY-12016
                 URL: https://issues.apache.org/jira/browse/GROOVY-12016
             Project: Groovy
          Issue Type: New Feature
          Components: Extension methods
            Reporter: Paul King
            Assignee: Paul King


h2. {{zipWithNext}} 
Add a {{zipWithNext}} GDK method returning successive adjacent pairs
(a sliding window of size 2, step 1), plus an overload taking a
combiner. Mirrors Kotlin's {{zipWithNext()}} / {{zipWithNext(transform)}}.
Today this needs the non-obvious, off-by-one-prone {{collate(2, 1, false)}}.

h3. Proposed signatures
{code:java}
List<Tuple2<T,T>>      zipWithNext(Iterable<T> self)
<R> List<R>            zipWithNext(Iterable<T> self, BiFunction<? super T,? 
super T,? extends R> combiner)
Iterator<Tuple2<T,T>>  zipWithNext(Iterator<T> self)                 // lazy
<R> Iterator<R>        zipWithNext(Iterator<T> self, BiFunction<? super T,? 
super T,? extends R> combiner)
List<Tuple2<T,T>>      zipWithNext(T[] self)
<R> List<R>            zipWithNext(T[] self, BiFunction<? super T,? super T,? 
extends R> combiner)
{code}

h3. Examples
{code:groovy}
[1, 2, 3, 4].zipWithNext()            == [new Tuple2(1,2), new Tuple2(2,3), new 
Tuple2(3,4)]
[1, 2, 3, 4].zipWithNext{ a,b -> b-a } == [1, 1, 1]            // pairwise 
deltas
[3,1,4,1,5].zipWithNext{ a,b -> a<=b }.every()  == false       // monotonic 
check
[].zipWithNext()  == []
[42].zipWithNext() == []                                       // no adjacent 
pair
{code}

h3. Notes
Groovy closures coerce to {{BiFunction}}. Primitive-array overloads
deferred (Tuple2 boxes anyway).

h2. {{groupConsecutive}}
Add a {{groupConsecutive}} GDK method splitting a sequence into maximal
runs of adjacent "same" elements (order preserved; the same key may
recur in separate runs). Unlike {{groupBy}} (a {{Map}}, global, loses
order and run boundaries) there is no native equivalent today.

h3. Proposed signatures
{code:java}
List<List<T>>      groupConsecutive(Iterable<T> self)                           
             // default equality
<K> List<List<T>>  groupConsecutive(Iterable<T> self, Function<? super T,? 
extends K> keyFn)  // by derived key
List<List<T>>      groupConsecutive(Iterable<T> self, BiPredicate<? super T,? 
super T> sameRun)
// + Iterator (lazy) and T[] receiver rows, same trio
{code}

h3. Examples
{code:groovy}
[1,1,2,2,2,3,1,1].groupConsecutive()        == [[1,1],[2,2,2],[3],[1,1]]   // 
two separate [1,1] runs
[1,1L,1.0,2,2].groupConsecutive()           == [[1,1L,1.0],[2,2]]          // 
number-aware default
['apple','avocado','banana'].groupConsecutive{ it[0] } == 
[['apple','avocado'],['banana']]
xs.groupConsecutive{ a,b -> Objects.equals(a,b) }                          // 
opt out to strict .equals()
'aaabbbcccd'.toList().groupConsecutive().collect{ [it[0], it.size()] }     // 
run-length encoding
{code}

h3. Design notes
* No-arg uses Groovy's number-aware equality ({{coercedEquals}}),
  consistent with {{unique()}} and the set-algebra family (derived from
  the DefaultGroovyMethods source; {{contains}}'s plain-equals is the
  documented anomaly, not the model).
* Opt out via the {{BiPredicate}} overload: {{Objects.equals}} for
  strict, {{(a <=> b) == 0}} for natural-order/value equivalence,
  {{cmp.compare(a,b)==0}} for a Comparator. A {{Comparator}} overload was
  rejected: a boolean closure silently mis-coerces to it.
* Arity-disambiguated overloads (0/1/2 params); no closure-arity sniffing.
* {{dedupeConsecutive}} intentionally NOT included — it is
  {{groupConsecutive()*.first()}} (cf. {{findAll}} with no {{rejectAll}}).




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to