From df4acd4b00879dd226ffdff4408f3ec7be304a75 Mon Sep 17 00:00:00 2001 From: Dmitrii Pevunov Date: Tue, 15 Jul 2025 00:47:12 +0400 Subject: [PATCH 1/2] nonEmptyTraverse for NEM with key mapping --- core/src/main/scala/cats/data/NonEmptyMapImpl.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/main/scala/cats/data/NonEmptyMapImpl.scala b/core/src/main/scala/cats/data/NonEmptyMapImpl.scala index 373f59f6a2..158dfd1502 100644 --- a/core/src/main/scala/cats/data/NonEmptyMapImpl.scala +++ b/core/src/main/scala/cats/data/NonEmptyMapImpl.scala @@ -261,6 +261,18 @@ sealed class NonEmptyMapOps[K, A](private[data] val value: NonEmptyMap[K, A]) { loop(head, tail).value } + def nonEmptyTraverse[G[_], L, B]( + f: (K, A) => G[(L, B)] + )(implicit G: Apply[G], ordL: Order[L]): G[NonEmptyMap[L, B]] = { + def loop(h: (K, A), t: SortedMap[K, A]): Eval[G[NonEmptyMap[L, B]]] = + if (t.isEmpty) + Eval.now(G.map(f.tupled(h))(b => NonEmptyMap(b, SortedMap.empty[L, B](ordL.toOrdering)))) + else + G.map2Eval(f.tupled(h), Eval.defer(loop(t.head, t.tail)))((lb, acc) => NonEmptyMap(lb, acc.toSortedMap)) + + loop(head, tail).value + } + /** * Typesafe stringification method. * From 08ce3b6e739c0dc023563c5e25db9940d3c906f6 Mon Sep 17 00:00:00 2001 From: dpevunov-cp Date: Tue, 17 Mar 2026 23:25:58 +0400 Subject: [PATCH 2/2] Update `nonEmptyTraverse` to `nonEmptyTraverseBoth` with refined implementation --- core/src/main/scala/cats/data/NonEmptyMapImpl.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala/cats/data/NonEmptyMapImpl.scala b/core/src/main/scala/cats/data/NonEmptyMapImpl.scala index 772912c9d2..fc0031da30 100644 --- a/core/src/main/scala/cats/data/NonEmptyMapImpl.scala +++ b/core/src/main/scala/cats/data/NonEmptyMapImpl.scala @@ -261,16 +261,18 @@ sealed class NonEmptyMapOps[K, A](private[data] val value: NonEmptyMap[K, A]) { loop(head, tail).value } - def nonEmptyTraverse[G[_], L, B]( + def nonEmptyTraverseBoth[G[_], L, B]( f: (K, A) => G[(L, B)] )(implicit G: Apply[G], ordL: Order[L]): G[NonEmptyMap[L, B]] = { - def loop(h: (K, A), t: SortedMap[K, A]): Eval[G[NonEmptyMap[L, B]]] = + def loop(h: (K, A), t: SortedMap[K, A]): Eval[G[(L, B, SortedMap[L, B])]] = if (t.isEmpty) - Eval.now(G.map(f.tupled(h))(b => NonEmptyMap(b, SortedMap.empty[L, B](ordL.toOrdering)))) + Eval.now(G.map(f.tupled(h)) { case (l, b) => (l, b, SortedMap.empty[L, B](ordL.toOrdering)) }) else - G.map2Eval(f.tupled(h), Eval.defer(loop(t.head, t.tail)))((lb, acc) => NonEmptyMap(lb, acc.toSortedMap)) + G.map2Eval(f.tupled(h), Eval.defer(loop(t.head, t.tail))) { case ((l, b), (al, ab, acc)) => + (l, b, acc + (al -> ab)) + } - loop(head, tail).value + G.map(loop(head, tail).value) { case (l, b, sm) => NonEmptyMap((l, b), sm) } } /**