Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions core/src/main/scala/cats/syntax/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,53 @@ final class ListOps[A](private val la: List[A]) extends AnyVal {
*/
def scanRightNel[B](b: B)(f: (A, B) => B): NonEmptyList[B] =
NonEmptyList.fromListUnsafe(la.scanRight(b)(f))

/**
* Split this List into a NonEmptyList of Lists based on a predicate.
* The behaviour is aimed to be identical to that of haskell's `splitWhen`
*
* {{{
* scala> import cats.syntax.all._
* scala> List(1, 1).splitWhen(_ == 1)
* val res0: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List(), List(), List())
*
* scala> List.empty[Int].splitWhen(_ == 1)
* val res1: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List())
*
* scala> List(1, 2, 3, 1, 4, 5).splitWhen(_ == 1)
* val res2: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List(), List(2, 3), List(4, 5))
* }}}
*/

def splitWhen(f: A => Boolean): NonEmptyList[List[A]] = {
la.reverse.foldLeft(NonEmptyList.one(List.empty[A])) { case (lst, e) =>
if (f(e)) Nil :: lst else NonEmptyList(e :: lst.head, lst.tail)
}
}

/**
* Split this List into a NonEmptyList of Lists based on the effectufl predicate. Monadic version of `splitWhen`
*
* {{{
* scala> import cats.syntax.all._, cats.Eval
* scala> List(1, 1).splitWhenM(x => Eval.now(x == 1)).value
* val res0: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List(), List(), List())
*
* scala> List.empty[Int].splitWhenM(x => Eval.now(x == 1)).value
* val res1: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List())
*
* scala> List(1, 2, 3, 1, 4, 5).splitWhenM(x => Eval.now(x == 1)).value
* val res2: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List(), List(2, 3), List(4, 5))
* }}}
*/

def splitWhenM[G[_]](f: A => G[Boolean])(implicit M: Monad[G]): G[NonEmptyList[List[A]]] = {
Comment thread
TheBugYouCantFix marked this conversation as resolved.
Outdated
la.reverse.foldLeft(M.pure(NonEmptyList.one(List.empty[A]))) { case (acc, e) =>
M.flatMap(acc) { case lst =>
M.map(f(e))(if (_) Nil :: lst else NonEmptyList(e :: lst.head, lst.tail))
}
}
}
}

private[syntax] trait ListSyntaxBinCompat0 {
Expand Down
23 changes: 23 additions & 0 deletions tests/shared/src/test/scala/cats/tests/ListSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,29 @@ class ListSuite extends CatsSuite {

assert(sumAll == lst.sum)
}

Comment thread
TheBugYouCantFix marked this conversation as resolved.
Outdated
test(s"splitWhen") {
forAll { (li: List[Int]) =>
val pred = (x: Int) => x > 0
val res = li.splitWhen(pred)
val expectedFiltered = li.filterNot(pred)
val expectedSize = li.size - expectedFiltered.size + 1
assert(res.size === expectedSize)
assert(res.toList.flatten === expectedFiltered)
}
}

test(s"splitWhenM") {
forAll { (li: List[Int]) =>
val pred = (x: Int) => x > 0
val predM = (x: Int) => Eval.now(pred(x))
val res = li.splitWhenM(predM)
val expectedFiltered = li.filterNot(pred)
val expectedSize = li.size - expectedFiltered.size + 1
assert(res.value.size === expectedSize)
assert(res.value.toList.flatten === expectedFiltered)
}
}
}

final class ListInstancesSuite extends munit.FunSuite {
Expand Down
Loading