Skip to content

Commit 5ee3767

Browse files
TheBugYouCantFixГорбанев Александр Владимирович
authored andcommitted
move splitWhen to list
1 parent 39f9ec6 commit 5ee3767

5 files changed

Lines changed: 70 additions & 74 deletions

File tree

core/src/main/scala/cats/Foldable.scala

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -960,49 +960,6 @@ trait Foldable[F[_]] extends UnorderedFoldable[F] with FoldableNFunctions[F] { s
960960
import cats.instances.either.*
961961
partitionBifoldM[G, Either, A, B, C](fa)(f)(A, M, Bifoldable[Either])
962962
}
963-
964-
/**
965-
* Split this Foldable into a List of Lists based on a predicate.
966-
* The behaviour is aimed to be identical to that of haskell's `splitWhen`
967-
*
968-
* {{{
969-
* scala> import cats.syntax.all._, cats.Foldable
970-
* scala> Foldable[List].splitWhen(List(1,1))(_ == 1)
971-
* res1: List[List[Int]] = List(List(), List(), List())
972-
* scala> Foldable[List].splitWhen(Nil)(_ == 1)
973-
* res2: List[List[Nothing]] = List(List())
974-
* scala> Foldable[List].splitWhen(List(1, 2, 3, 1, 4, 5))(_ == 1)
975-
* res3: List[List[Int]] = List(List(), List(2, 3), List(4, 5))
976-
* }}}
977-
*/
978-
979-
def splitWhen[A](fa: F[A])(f: A => Boolean): List[List[A]] = {
980-
toList(fa).reverse.foldLeft(List.empty[A] :: Nil) { case (lst, e) =>
981-
if (f(e)) Nil :: lst else (e :: lst.head) :: lst.tail
982-
}
983-
}
984-
985-
/**
986-
* Split this Foldable into a List of Lists based on the effectufl predicate. Monadic version of `splitWhen`
987-
*
988-
* {{{
989-
* scala> import cats.syntax.all._, cats.Foldable, cats.Eval
990-
* scala> Foldable[List].splitWhenM(List(1,1))(x => Eval.now(x == 1)).value
991-
* res1: List[List[Int]] = List(List(), List(), List())
992-
* scala> Foldable[List].splitWhenM(List.empty[Int])(x => Eval.now(x == 1)).value
993-
* res2: List[List[Int]] = List(List())
994-
* scala> Foldable[List].splitWhenM(List(1, 2, 3, 1, 4, 5))(x => Eval.now(x == 1)).value
995-
* val res3: List[List[Int]] = List(List(), List(2, 3), List(4, 5))
996-
* }}}
997-
*/
998-
999-
def splitWhenM[G[_], A](fa: F[A])(f: A => G[Boolean])(implicit M: Monad[G]): G[List[List[A]]] = {
1000-
toList(fa).reverse.foldLeft(M.pure(List.empty[A] :: Nil)) { case (acc, e) =>
1001-
M.flatMap(acc) { case lst =>
1002-
M.map(f(e))(if (_) Nil :: lst else (e :: lst.head) :: lst.tail)
1003-
}
1004-
}
1005-
}
1006963
}
1007964

1008965
object Foldable {

core/src/main/scala/cats/syntax/foldable.scala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -304,14 +304,6 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
304304
)(implicit A: Alternative[F], F: Foldable[F], M: Monad[G]): G[(F[B], F[C])] =
305305
F.partitionEitherM[G, A, B, C](fa)(f)(A, M)
306306

307-
def splitWhen(f: A => Boolean)(implicit F: Foldable[F]): List[List[A]] = {
308-
F.splitWhen[A](fa)(f)
309-
}
310-
311-
def splitWhenM[G[_]](f: A => G[Boolean])(implicit F: Foldable[F], G: Monad[G]): G[List[List[A]]] = {
312-
F.splitWhenM[G, A](fa)(f)(G)
313-
}
314-
315307
def sliding2(implicit F: Foldable[F]): List[(A, A)] =
316308
F.sliding2(fa)
317309
def sliding3(implicit F: Foldable[F]): List[(A, A, A)] =

core/src/main/scala/cats/syntax/list.scala

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,53 @@ final class ListOps[A](private val la: List[A]) extends AnyVal {
139139
*/
140140
def scanRightNel[B](b: B)(f: (A, B) => B): NonEmptyList[B] =
141141
NonEmptyList.fromListUnsafe(la.scanRight(b)(f))
142+
143+
/**
144+
* Split this List into a NonEmptyList of Lists based on a predicate.
145+
* The behaviour is aimed to be identical to that of haskell's `splitWhen`
146+
*
147+
* {{{
148+
* scala> import cats.syntax.all._
149+
* scala> List(1, 1).splitWhen(_ == 1)
150+
* val res0: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List(), List(), List())
151+
*
152+
* scala> List.empty[Int].splitWhen(_ == 1)
153+
* val res1: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List())
154+
*
155+
* scala> List(1, 2, 3, 1, 4, 5).splitWhen(_ == 1)
156+
* val res2: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List(), List(2, 3), List(4, 5))
157+
* }}}
158+
*/
159+
160+
def splitWhen(f: A => Boolean): NonEmptyList[List[A]] = {
161+
la.reverse.foldLeft(NonEmptyList.one(List.empty[A])) { case (lst, e) =>
162+
if (f(e)) Nil :: lst else NonEmptyList(e :: lst.head, lst.tail)
163+
}
164+
}
165+
166+
/**
167+
* Split this List into a NonEmptyList of Lists based on the effectufl predicate. Monadic version of `splitWhen`
168+
*
169+
* {{{
170+
* scala> import cats.syntax.all._, cats.Eval
171+
* scala> List(1, 1).splitWhenM(x => Eval.now(x == 1)).value
172+
* val res0: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List(), List(), List())
173+
*
174+
* scala> List.empty[Int].splitWhenM(x => Eval.now(x == 1)).value
175+
* val res1: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List())
176+
*
177+
* scala> List(1, 2, 3, 1, 4, 5).splitWhenM(x => Eval.now(x == 1)).value
178+
* val res2: cats.data.NonEmptyList[List[Int]] = NonEmptyList(List(), List(2, 3), List(4, 5))
179+
* }}}
180+
*/
181+
182+
def splitWhenM[G[_]](f: A => G[Boolean])(implicit M: Monad[G]): G[NonEmptyList[List[A]]] = {
183+
la.reverse.foldLeft(M.pure(NonEmptyList.one(List.empty[A]))) { case (acc, e) =>
184+
M.flatMap(acc) { case lst =>
185+
M.map(f(e))(if (_) Nil :: lst else NonEmptyList(e :: lst.head, lst.tail))
186+
}
187+
}
188+
}
142189
}
143190

144191
private[syntax] trait ListSyntaxBinCompat0 {

tests/shared/src/test/scala/cats/tests/FoldableSuite.scala

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -333,29 +333,6 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit
333333
}
334334
}
335335

336-
test(s"Foldable[$name].splitWhen") {
337-
forAll { (fa: F[Int]) =>
338-
val pred = (x: Int) => x > 0
339-
val res = fa.splitWhen(pred)
340-
val expectedFiltered = iterator(fa).filterNot(pred).toList
341-
val expectedSize = fa.size - expectedFiltered.size + 1
342-
assert(res.size.toLong === expectedSize)
343-
assert(res.flatten === expectedFiltered)
344-
}
345-
}
346-
347-
test(s"Foldable[$name].splitWhenM") {
348-
forAll { (fa: F[Int]) =>
349-
val pred = (x: Int) => x > 0
350-
val predM = (x: Int) => Eval.now(pred(x))
351-
val res = fa.splitWhenM(predM)
352-
val expectedFiltered = iterator(fa).filterNot(pred).toList
353-
val expectedSize = fa.size - expectedFiltered.size + 1
354-
assert(res.value.size.toLong === expectedSize)
355-
assert(res.value.flatten === expectedFiltered)
356-
}
357-
}
358-
359336
test(s"Foldable[$name].sliding2 consistent with List#sliding(2)") {
360337
forAll { (fi: F[Int]) =>
361338
val n = 2

tests/shared/src/test/scala/cats/tests/ListSuite.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,29 @@ class ListSuite extends CatsSuite {
137137

138138
assert(sumAll == lst.sum)
139139
}
140+
141+
test(s"splitWhen") {
142+
forAll { (li: List[Int]) =>
143+
val pred = (x: Int) => x > 0
144+
val res = li.splitWhen(pred)
145+
val expectedFiltered = li.filterNot(pred)
146+
val expectedSize = li.size - expectedFiltered.size + 1
147+
assert(res.size === expectedSize)
148+
assert(res.toList.flatten === expectedFiltered)
149+
}
150+
}
151+
152+
test(s"splitWhenM") {
153+
forAll { (li: List[Int]) =>
154+
val pred = (x: Int) => x > 0
155+
val predM = (x: Int) => Eval.now(pred(x))
156+
val res = li.splitWhenM(predM)
157+
val expectedFiltered = li.filterNot(pred)
158+
val expectedSize = li.size - expectedFiltered.size + 1
159+
assert(res.value.size === expectedSize)
160+
assert(res.value.toList.flatten === expectedFiltered)
161+
}
162+
}
140163
}
141164

142165
final class ListInstancesSuite extends munit.FunSuite {

0 commit comments

Comments
 (0)