How to split a sequence into two pieces by predicate?
ScalaScala Problem Overview
How do I split a sequence into two lists by a predicate?
Alternative: I can use filter
and filterNot
, or write my own method, but isn't there a better more general (built-in) method ?
Scala Solutions
Solution 1 - Scala
By using partition
method:
scala> List(1,2,3,4).partition(x => x % 2 == 0)
res0: (List[Int], List[Int]) = (List(2, 4),List(1, 3))
Solution 2 - Scala
Good that partition
was the thing you wanted -- there's another method that also uses a predicate to split a list in two: span
.
The first one, partition will put all "true" elements in one list, and the others in the second list.
span will put all elements in one list until an element is "false" (in terms of the predicate). From that point forward, it will put the elements in the second list.
scala> Seq(1,2,3,4).span(x => x % 2 == 0)
res0: (Seq[Int], Seq[Int]) = (List(),List(1, 2, 3, 4))
Solution 3 - Scala
You might want to take a look at scalex.org - it allows you to search the scala standard library for functions by their signature. For example, type the following:
List[A] => (A => Boolean) => (List[A], List[A])
You would see partition.
Solution 4 - Scala
You can also use foldLeft if you need something a little extra. I just wrote some code like this when partition didn't cut it:
val list:List[Person] = /* get your list */
val (students,teachers) =
list.foldLeft(List.empty[Student],List.empty[Teacher]) {
case ((acc1, acc2), p) => p match {
case s:Student => (s :: acc1, acc2)
case t:Teacher => (acc1, t :: acc2)
}
}
Solution 5 - Scala
I know I might be late for the party and there are more specific answers, but you could make good use of groupBy
val ret = List(1,2,3,4).groupBy(x => x % 2 == 0)
ret: scala.collection.immutable.Map[Boolean,List[Int]] = Map(false -> List(1, 3), true -> List(2, 4))
ret(true)
res3: List[Int] = List(2, 4)
ret(false)
res4: List[Int] = List(1, 3)
This makes your code a bit more future-proof if you need to change the condition into something non boolean.
Solution 6 - Scala
If you want to split a list into more than 2 pieces, and ignore the bounds, you could use something like this (modify if you need to search for ints)
def split(list_in: List[String], search: String): List[List[String]] = {
def split_helper(accum: List[List[String]], list_in2: List[String], search: String): List[List[String]] = {
val (h1, h2) = list_in2.span({x: String => x!= search})
val new_accum = accum :+ h1
if (h2.contains(search)) {
return split_helper(new_accum, h2.drop(1), search)
}
else {
return accum
}
}
return split_helper(List(), list_in, search)
}
// TEST
// split(List("a", "b", "c", "d", "c", "a"), {x: String => x != "x"})