Skip to content

Add groupMap family of methods to NonEmptyVector and NonEmptySeq#4826

Open
arnavsharma990 wants to merge 1 commit intotypelevel:mainfrom
arnavsharma990:feature/groupmap-nonemptyvector-nonemptyseq
Open

Add groupMap family of methods to NonEmptyVector and NonEmptySeq#4826
arnavsharma990 wants to merge 1 commit intotypelevel:mainfrom
arnavsharma990:feature/groupmap-nonemptyvector-nonemptyseq

Conversation

@arnavsharma990
Copy link
Contributor

@arnavsharma990 arnavsharma990 commented Feb 20, 2026

Motivation

NonEmptyList and NonEmptyChain both have a full set of groupMap methods available:

Method Returns
groupMap SortedMap[K, NonEmptyList[B]]
groupMapNem NonEmptyMap[K, NonEmptyList[B]]
groupMapReduce SortedMap[K, B]
groupMapReduceNem NonEmptyMap[K, B]
groupMapReduceWith SortedMap[K, B]
groupMapReduceWithNem NonEmptyMap[K, B]

NonEmptyVector and NonEmptySeq only had groupBy/groupByNem, leading to an inconsistency in the API between the four main NonEmpty* collection types.

Changes

Adds all six missing methods to both NonEmptyVector and NonEmptySeq:

  • groupMap/groupMapNem - group and transform elements, returning NonEmptyVector[B]/NonEmptySeq[B] per group
  • groupMapReduce/groupMapReduceNem - group and transform and reduce using Semigroup
  • groupMapReduceWith/groupMapReduceWithNem - group and transform and reduce using explicit combine function

…uceWith/groupMapReduceWithNem to NonEmptyVector and NonEmptySeq

NonEmptyList and NonEmptyChain both expose a rich groupMap family of methods:
  - groupMap           / groupMapNem
  - groupMapReduce     / groupMapReduceNem
  - groupMapReduceWith / groupMapReduceWithNem

NonEmptyVector and NonEmptySeq only had groupBy / groupByNem, creating an
inconsistency across the four primary NonEmpty collection types.

This commit brings NonEmptyVector and NonEmptySeq up to parity by adding all
six missing methods to each, following the same implementation pattern used
in NonEmptyList (explicit TreeMap accumulation loop) and the same scaladoc
style used throughout the codebase.
Copilot AI review requested due to automatic review settings February 20, 2026 09:06
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fills an API gap by adding the full groupMap family of grouping/transformation/reduction methods to NonEmptyVector and NonEmptySeq, bringing them in line with NonEmptyList and NonEmptyChain.

Changes:

  • Add groupMap / groupMapNem to NonEmptyVector and NonEmptySeq.
  • Add groupMapReduce / groupMapReduceNem to NonEmptyVector and NonEmptySeq.
  • Add groupMapReduceWith / groupMapReduceWithNem to NonEmptyVector and NonEmptySeq, following existing TreeMap accumulation patterns and providing scaladoc examples.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
core/src/main/scala/cats/data/NonEmptyVector.scala Adds the groupMap* method family returning SortedMap/NonEmptyMap with NonEmptyVector group values.
core/src/main/scala/cats/data/NonEmptySeq.scala Adds the groupMap* method family returning SortedMap/NonEmptyMap with NonEmptySeq group values.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 310 to 316
final def groupBy[B](f: A => B)(implicit B: Order[B]): SortedMap[B, NonEmptyVector[A]] = {
implicit val ordering: Ordering[B] = B.toOrdering
var m = TreeMap.empty[B, mutable.Builder[A, Vector[A]]]

for { elem <- toVector } {
val k = f(elem)

Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that groupMap exists, groupBy duplicates almost the same TreeMap/builder accumulation logic. Consider re-implementing groupBy as groupMap(key = f)(identity) to reduce duplication and ensure future changes to the grouping algorithm stay consistent across both methods (similar to NonEmptyList’s groupBy delegating to groupMap).

Copilot uses AI. Check for mistakes.
Comment on lines +362 to +378
final def groupMap[K, B](key: A => K)(f: A => B)(implicit K: Order[K]): SortedMap[K, NonEmptyVector[B]] = {
implicit val ordering: Ordering[K] = K.toOrdering
var m = TreeMap.empty[K, mutable.Builder[B, Vector[B]]]

for { elem <- toVector } {
val k = key(elem)

m.get(k) match {
case None => m += ((k, Vector.newBuilder[B] += f(elem)))
case Some(builder) => builder += f(elem)
}
}

m.map { case (k, v) =>
(k, NonEmptyVector.fromVectorUnsafe(v.result()))
}: TreeMap[K, NonEmptyVector[B]]
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new groupMap* methods add non-trivial behavior but there are currently no unit/property tests covering them for NonEmptyVector (unlike NonEmptyList, which has explicit groupMap/groupMapReduce* tests). Please add tests in NonEmptyVectorSuite covering groupMap, groupMapNem, groupMapReduce, groupMapReduceNem, groupMapReduceWith, and groupMapReduceWithNem (e.g., consistency with toVector.toList.groupBy + map/reduce).

Copilot uses AI. Check for mistakes.
Comment on lines 317 to 323
final def groupBy[B](f: A => B)(implicit B: Order[B]): SortedMap[B, NonEmptySeq[A]] = {
implicit val ordering: Ordering[B] = B.toOrdering
var m = TreeMap.empty[B, mutable.Builder[A, Seq[A]]]

for { elem <- toSeq } {
val k = f(elem)

Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that groupMap exists, groupBy duplicates almost the same TreeMap/builder accumulation logic. Consider re-implementing groupBy as groupMap(key = f)(identity) to reduce duplication and keep the grouping algorithm consistent across both methods (similar to how NonEmptyList’s groupBy delegates to groupMap).

Copilot uses AI. Check for mistakes.
Comment on lines +369 to +385
final def groupMap[K, B](key: A => K)(f: A => B)(implicit K: Order[K]): SortedMap[K, NonEmptySeq[B]] = {
implicit val ordering: Ordering[K] = K.toOrdering
var m = TreeMap.empty[K, mutable.Builder[B, Seq[B]]]

for { elem <- toSeq } {
val k = key(elem)

m.get(k) match {
case None => m += ((k, Seq.newBuilder[B] += f(elem)))
case Some(builder) => builder += f(elem)
}
}

m.map { case (k, v) =>
(k, NonEmptySeq.fromSeqUnsafe(v.result()))
}: TreeMap[K, NonEmptySeq[B]]
}
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new groupMap* methods add non-trivial behavior but there are currently no unit/property tests covering them for NonEmptySeq (unlike NonEmptyList, which has explicit groupMap/groupMapReduce* tests). Please add tests in NonEmptySeqSuite covering groupMap, groupMapNem, groupMapReduce, groupMapReduceNem, groupMapReduceWith, and groupMapReduceWithNem (e.g., consistency with toSeq.toList.groupBy + map/reduce).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants