diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index 70bbbe3462..e8f07038bb 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -37,7 +37,7 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * Example: * {{{ * scala> import cats.data.NonEmptyList - * scala> import cats.implicits._ + * scala> import cats.syntax.list._ * * scala> val result1: List[Int] = List(1, 2) * scala> result1.toNel @@ -57,7 +57,7 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * {{{ * scala> import cats.data.NonEmptyList * scala> import scala.collection.immutable.SortedMap - * scala> import cats.implicits._ + * scala> import cats.syntax.all._ * * scala> val list = List(12, -2, 3, -5) * @@ -79,7 +79,7 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * {{{ * scala> import cats.data.NonEmptyList * scala> import scala.collection.immutable.SortedMap - * scala> import cats.implicits._ + * scala> import cats.syntax.all._ * * scala> val list = List(12, -2, 3, -5) * @@ -98,6 +98,35 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { ) } + /** + * Groups elements inside this [[List]] according to the [[Order]] + * of the keys produced by the given key function. + * And each element in a group is transformed into a value of type `B` + * using the mapping function. + * + * {{{ + * scala> import scala.collection.immutable.SortedMap + * scala> import cats.data.NonEmptyList + * scala> import cats.syntax.all._ + * + * scala> val list = List(12, -2, 3, -5) + * scala> val expectedResult = SortedMap(false -> NonEmptyList.of("-2", "-5"), true -> NonEmptyList.of("12", "3")) + * scala> val result = list.groupByNelMap(_ >= 0, _.toString) + * scala> result === expectedResult + * res0: Boolean = true + * }}} + */ + def groupByNelMap[K, B](key: A => K, f: A => B)(implicit K: Order[K]): SortedMap[K, NonEmptyList[B]] = { + implicit val ordering: Ordering[K] = K.toOrdering + + toNel match { + case None => + SortedMap.empty[K, NonEmptyList[B]] + case Some(nel) => + nel.groupMap(key)(f) + } + } + /** * Produces a `NonEmptyList` containing cumulative results of applying the * operator going left to right. @@ -105,7 +134,7 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * Example: * {{{ * scala> import cats.data.NonEmptyList - * scala> import cats.implicits._ + * scala> import cats.syntax.all._ * * scala> val result1: List[Int] = List(1, 2) * scala> result1.scanLeftNel(100)(_ + _) @@ -126,7 +155,7 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * Example: * {{{ * scala> import cats.data.NonEmptyList - * scala> import cats.implicits._ + * scala> import cats.syntax.all._ * * scala> val result1: List[Int] = List(1, 2) * scala> result1.scanRightNel(100)(_ + _) @@ -154,7 +183,7 @@ final private[syntax] class ListOpsBinCompat0[A](private val la: List[A]) extend * {{{ * scala> import cats.data.NonEmptyChain * scala> import scala.collection.immutable.SortedMap - * scala> import cats.implicits._ + * scala> import cats.syntax.all._ * * scala> val list = List(12, -2, 3, -5) * diff --git a/tests/shared/src/test/scala/cats/tests/ListSuite.scala b/tests/shared/src/test/scala/cats/tests/ListSuite.scala index 8bb06f2d22..318a9c01df 100644 --- a/tests/shared/src/test/scala/cats/tests/ListSuite.scala +++ b/tests/shared/src/test/scala/cats/tests/ListSuite.scala @@ -71,12 +71,12 @@ class ListSuite extends CatsSuite { test("nel => list => nel returns original nel")( forAll { (fa: NonEmptyList[Int]) => - assert(fa.toList.toNel === (Some(fa))) + assert(fa.toList.toNel === Some(fa)) } ) test("toNel on empty list returns None") { - assert(List.empty[Int].toNel === None) + assertEquals(List.empty[Int].toNel, None) } test("groupByNel should be consistent with groupBy")( @@ -85,9 +85,19 @@ class ListSuite extends CatsSuite { } ) + test("groupByNelMap should be consistent with groupBy + map")( + forAll { (fa: List[Int], f: Int => Int, g: Int => Int) => + assert( + (fa.groupByNelMap(f, g).map { case (k, v) => (k, v.toList) }: Map[Int, List[Int]]) === fa + .groupBy(f) + .map { case (k, v) => (k, v.map(g)) } + ) + } + ) + test("groupByNelA should be consistent with groupByNel")( forAll { (fa: List[Int], f: Int => Int) => - assert(fa.groupByNelA(f.andThen(Option(_))) === (Option(fa.groupByNel(f)))) + assert(fa.groupByNelA(f.andThen(Option(_))) === Option(fa.groupByNel(f))) } ) @@ -104,10 +114,10 @@ class ListSuite extends CatsSuite { ) test("show") { - assert(List(1, 2, 3).show === "List(1, 2, 3)") - assert((Nil: List[Int]).show === "List()") + assertEquals(List(1, 2, 3).show, "List(1, 2, 3)") + assertEquals((Nil: List[Int]).show, "List()") forAll { (l: List[String]) => - assert(l.show === (l.toString)) + assert(l.show === l.toString) } } @@ -118,7 +128,7 @@ class ListSuite extends CatsSuite { .apply() .sum - assert(sumAll == lst.sum) + assertEquals(sumAll, lst.sum) } }