Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NonEmptyList.groupByNelA #3432

Merged
merged 12 commits into from
Jun 7, 2020
27 changes: 26 additions & 1 deletion core/src/main/scala/cats/syntax/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cats
package syntax

import cats.data.{NonEmptyChain, NonEmptyList}

import scala.collection.immutable.SortedMap

trait ListSyntax {
Expand Down Expand Up @@ -52,6 +51,32 @@ 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
Copy link
Member

Choose a reason for hiding this comment

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

btw you don't need to do any explicit assertions here, you could just do

scala> list.groupByNelA(num => Option(0).map(num >= _))
res0: Option(SortedMap(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3)))

which will be a little less noise for the reader, while still asserting that the resulting expression is the same as what you typed. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, this one is pretty interesting

[info] > Labels of failing property: 

[info] 'Some(TreeMap(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3)))' is not equal to 'Some(Map(false -> NonEmptyList(-2, -5), true -> NonEmptyList(12, 3)))'

this seems to fail only on Scala 2.13.1 🤔 works for me locally and on the rest of Scala versions https://travis-ci.org/github/typelevel/cats/builds/691476028

Copy link
Member

Choose a reason for hiding this comment

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

It works for you locally on 2.13?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

2.12.10

Copy link
Contributor Author

Choose a reason for hiding this comment

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

quite not sure how to approach this - is there anything I can do to work it out or should I just stay with the explicit assertion?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is an issue with toString changing for some collection types between 2.12 and 2.13. Unfortunately I think you have to do something like this: #3155

* res0: Boolean = true
* }}}
*/
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)))
)
}

/** Produces a `NonEmptyList` containing cumulative results of applying the
* operator going left to right.
*
Expand Down
6 changes: 6 additions & 0 deletions tests/src/test/scala/cats/tests/ListSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,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()")
Expand Down