How to split a sequence into two pieces by predicate?

Scala

Scala 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"})

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJohn ThreepwoodView Question on Stackoverflow
Solution 1 - Scalaom-nom-nomView Answer on Stackoverflow
Solution 2 - ScalaDaniel C. SobralView Answer on Stackoverflow
Solution 3 - Scalaoxbow_lakesView Answer on Stackoverflow
Solution 4 - ScalanairbvView Answer on Stackoverflow
Solution 5 - ScalaGabberView Answer on Stackoverflow
Solution 6 - ScalaMattView Answer on Stackoverflow