From 5c64869c4c797058f34681b0a3e2afd5cabf6448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kiersznowski?= Date: Mon, 25 May 2020 22:42:22 +0200 Subject: [PATCH 01/11] adding groupByNelA implementation --- core/src/main/scala/cats/syntax/list.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index 0891a2f1bf..bd59039a8b 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -52,6 +52,13 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { toNel.fold(SortedMap.empty[B, NonEmptyList[A]])(_.groupBy(f)) } + def groupByNelA[F[_], B](f: A => F[B])(implicit B: Order[B], F: Applicative[F]): F[SortedMap[B, NonEmptyList[A]]] = { + implicit val ordering: Ordering[B] = B.toOrdering + + toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel => F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(_.groupBy(_._2).mapValues(_.map(_._1)))) + } + + /** Produces a `NonEmptyList` containing cumulative results of applying the * operator going left to right. * From 3bb21f856a294a894311ae91ac7d05161943321d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kiersznowski?= Date: Mon, 25 May 2020 23:46:41 +0200 Subject: [PATCH 02/11] adding groupByNelA documentation --- core/src/main/scala/cats/syntax/list.scala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index bd59039a8b..b0af0a8890 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -52,6 +52,23 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { toNel.fold(SortedMap.empty[B, NonEmptyList[A]])(_.groupBy(f)) } + /** + * Groups elements inside this `List` according to the `Order` of the keys + * produced by the given mapping monadic function. + * + * {{{ + * scala> import cats.data.NonEmptyList + * scala> import scala.collection.immutable.SortedMap + * scala> import cats.implicits._ + * + * scala> val list = List(12, -2, 3, -5) + * + * scala> val expectedResult = Option(SortedMap(false -> NonEmptyList.of(-2, -5), true -> NonEmptyList.of(12, 3))) + * + * scala> list.groupByNelA(num => Option(0).map(num >= _)) === expectedResult + * res0: Boolean = true + * }}} + */ def groupByNelA[F[_], B](f: A => F[B])(implicit B: Order[B], F: Applicative[F]): F[SortedMap[B, NonEmptyList[A]]] = { implicit val ordering: Ordering[B] = B.toOrdering From 6bc955d5c4e7b4800fefcfd45db832867e9b5b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kiersznowski?= Date: Tue, 26 May 2020 00:11:12 +0200 Subject: [PATCH 03/11] adding groupByNelA test for consistency with groupByNel --- tests/src/test/scala/cats/tests/ListSuite.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/src/test/scala/cats/tests/ListSuite.scala b/tests/src/test/scala/cats/tests/ListSuite.scala index 22827b960c..1541a67910 100644 --- a/tests/src/test/scala/cats/tests/ListSuite.scala +++ b/tests/src/test/scala/cats/tests/ListSuite.scala @@ -59,6 +59,12 @@ class ListSuite extends CatsSuite { } ) + test("groupByNelA should be consistent with groupByNel")( + forAll { (fa: List[Int], f: Int => Int) => + fa.groupByNelA(f.andThen(Option(_))) should === (Option(fa.groupByNel(f))) + } + ) + test("show") { List(1, 2, 3).show should ===("List(1, 2, 3)") (Nil: List[Int]).show should ===("List()") From 3d9b7954b1a7de1cc84bcef425eb8f87ff683e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kiersznowski?= Date: Tue, 26 May 2020 00:15:35 +0200 Subject: [PATCH 04/11] formatting --- core/src/main/scala/cats/syntax/list.scala | 5 +++-- tests/src/test/scala/cats/tests/ListSuite.scala | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index b0af0a8890..42f41f2b08 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -72,10 +72,11 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { def groupByNelA[F[_], B](f: A => F[B])(implicit B: Order[B], F: Applicative[F]): F[SortedMap[B, NonEmptyList[A]]] = { implicit val ordering: Ordering[B] = B.toOrdering - toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel => F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(_.groupBy(_._2).mapValues(_.map(_._1)))) + toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel => + F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(_.groupBy(_._2).mapValues(_.map(_._1))) + ) } - /** Produces a `NonEmptyList` containing cumulative results of applying the * operator going left to right. * diff --git a/tests/src/test/scala/cats/tests/ListSuite.scala b/tests/src/test/scala/cats/tests/ListSuite.scala index f9719bf318..009ef5085e 100644 --- a/tests/src/test/scala/cats/tests/ListSuite.scala +++ b/tests/src/test/scala/cats/tests/ListSuite.scala @@ -64,7 +64,7 @@ class ListSuite extends CatsSuite { test("groupByNelA should be consistent with groupByNel")( forAll { (fa: List[Int], f: Int => Int) => - fa.groupByNelA(f.andThen(Option(_))) should === (Option(fa.groupByNel(f))) + fa.groupByNelA(f.andThen(Option(_))) should ===(Option(fa.groupByNel(f))) } ) From 4ca92bb1d9df83033fe600f68b97e1f61286d41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kiersznowski?= Date: Tue, 26 May 2020 21:26:59 +0200 Subject: [PATCH 05/11] use Functor[SortedMap[B, ?]].map instead of mapValues --- core/src/main/scala/cats/syntax/list.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index 42f41f2b08..6b6ca4bd2c 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -2,7 +2,6 @@ package cats package syntax import cats.data.{NonEmptyChain, NonEmptyList} - import scala.collection.immutable.SortedMap trait ListSyntax { @@ -73,7 +72,9 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { implicit val ordering: Ordering[B] = B.toOrdering toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel => - F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(_.groupBy(_._2).mapValues(_.map(_._1))) + F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(list => + Functor[SortedMap[B, ?]].map(list.groupBy(_._2))(_.map(_._1)) + ) ) } From 026b5b3056c7db5c9a29b644717aaa583be5e6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kiersznowski?= Date: Tue, 26 May 2020 21:29:26 +0200 Subject: [PATCH 06/11] replacing `?` with `*` --- core/src/main/scala/cats/syntax/list.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index 6b6ca4bd2c..318ea65b5c 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -73,7 +73,7 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel => F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(list => - Functor[SortedMap[B, ?]].map(list.groupBy(_._2))(_.map(_._1)) + Functor[SortedMap[B, *]].map(list.groupBy(_._2))(_.map(_._1)) ) ) } From 14a0f3dca865f7629f8786434697b1c3cf9e0679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kiersznowski?= Date: Tue, 26 May 2020 22:49:27 +0200 Subject: [PATCH 07/11] more readable documentation --- core/src/main/scala/cats/syntax/list.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index 318ea65b5c..64b2897950 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -64,8 +64,8 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * * scala> val expectedResult = Option(SortedMap(false -> NonEmptyList.of(-2, -5), true -> NonEmptyList.of(12, 3))) * - * scala> list.groupByNelA(num => Option(0).map(num >= _)) === expectedResult - * res0: Boolean = true + * scala> list.groupByNelA(num => Option(0).map(num >= _)) + * res0: Option[SortedMap[Boolean, NonEmptyList[Int]]] = Some(Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3))) * }}} */ def groupByNelA[F[_], B](f: A => F[B])(implicit B: Order[B], F: Applicative[F]): F[SortedMap[B, NonEmptyList[A]]] = { From c131bf808f4bf1b32352b953ca1e163d42901585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kiersznowski?= Date: Wed, 27 May 2020 19:13:42 +0200 Subject: [PATCH 08/11] reordered implicits --- core/src/main/scala/cats/syntax/list.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index 64b2897950..521a2d17c4 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -68,7 +68,7 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * res0: Option[SortedMap[Boolean, NonEmptyList[Int]]] = Some(Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3))) * }}} */ - def groupByNelA[F[_], B](f: A => F[B])(implicit B: Order[B], F: Applicative[F]): F[SortedMap[B, NonEmptyList[A]]] = { + def groupByNelA[F[_], B](f: A => F[B])(implicit F: Applicative[F], B: Order[B]): F[SortedMap[B, NonEmptyList[A]]] = { implicit val ordering: Ordering[B] = B.toOrdering toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel => From f0f5b8fd1051e10ca712a4b1523411c3207f7b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Kiersznowski?= Date: Wed, 27 May 2020 19:30:56 +0200 Subject: [PATCH 09/11] functor instance moved out of fold --- core/src/main/scala/cats/syntax/list.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index 521a2d17c4..f3399b8f0c 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -68,12 +68,14 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * res0: Option[SortedMap[Boolean, NonEmptyList[Int]]] = Some(Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3))) * }}} */ + import cats.instances.sortedMap.catsStdInstancesForSortedMap def groupByNelA[F[_], B](f: A => F[B])(implicit F: Applicative[F], B: Order[B]): F[SortedMap[B, NonEmptyList[A]]] = { implicit val ordering: Ordering[B] = B.toOrdering + val functor = Functor[SortedMap[B, *]] toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel => F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(list => - Functor[SortedMap[B, *]].map(list.groupBy(_._2))(_.map(_._1)) + functor.map(list.groupBy(_._2))(_.map(_._1)) ) ) } From 0f6aadd9a2bf5e6db447d5cdc5460d5493a99cea Mon Sep 17 00:00:00 2001 From: root Date: Wed, 27 May 2020 20:42:16 +0200 Subject: [PATCH 10/11] formatting --- core/src/main/scala/cats/syntax/list.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index f3399b8f0c..68767346d2 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -68,15 +68,12 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * res0: Option[SortedMap[Boolean, NonEmptyList[Int]]] = Some(Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3))) * }}} */ - import cats.instances.sortedMap.catsStdInstancesForSortedMap def groupByNelA[F[_], B](f: A => F[B])(implicit F: Applicative[F], B: Order[B]): F[SortedMap[B, NonEmptyList[A]]] = { implicit val ordering: Ordering[B] = B.toOrdering val functor = Functor[SortedMap[B, *]] toNel.fold(F.pure(SortedMap.empty[B, NonEmptyList[A]]))(nel => - F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(list => - functor.map(list.groupBy(_._2))(_.map(_._1)) - ) + F.map(nel.traverse(a => F.tupleLeft(f(a), a)))(list => functor.map(list.groupBy(_._2))(_.map(_._1))) ) } From 4a67159c6d824a57a07bc84b7b4811c84a21ae31 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 27 May 2020 20:45:22 +0200 Subject: [PATCH 11/11] fixed doctest --- core/src/main/scala/cats/syntax/list.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/syntax/list.scala b/core/src/main/scala/cats/syntax/list.scala index 68767346d2..fee7388ca1 100644 --- a/core/src/main/scala/cats/syntax/list.scala +++ b/core/src/main/scala/cats/syntax/list.scala @@ -64,8 +64,8 @@ final class ListOps[A](private val la: List[A]) extends AnyVal { * * scala> val expectedResult = Option(SortedMap(false -> NonEmptyList.of(-2, -5), true -> NonEmptyList.of(12, 3))) * - * scala> list.groupByNelA(num => Option(0).map(num >= _)) - * res0: Option[SortedMap[Boolean, NonEmptyList[Int]]] = Some(Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3))) + * scala> list.groupByNelA(num => Option(0).map(num >= _)) === expectedResult + * res0: Boolean = true * }}} */ def groupByNelA[F[_], B](f: A => F[B])(implicit F: Applicative[F], B: Order[B]): F[SortedMap[B, NonEmptyList[A]]] = {