-1

Is there a built-in function (or an easy implementation) for splitting a list into sublists based on the element value

For example, if the list is:

val myList = List("a","b","c","","e","d","a","","d","a")

and I want to split it whenever the element '' occurs (in my case I'd also remove such element, but in general it could be grouped with either the previous or following sublist), obtaining the output:

List(List("a","b","c"),List("e","d","a"),List("d","a"))

I've been digging in stack and google for an answer, but surprisingly I haven't been able to find an answer.

If it's of any help, this is something that in Mathematica I'd solve as:

SplitBy[myList, # != "" &]

(or almost equivalently using Split)

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
Fraccalo
  • 197
  • 2
  • 12

2 Answers2

4

AFAIK, such function doesn't exist on the stdlib but you can create your own very easily.

def splitBy[A](list: List[A])(p: A => Boolean): List[List[A]] = {
  @annotation.tailrec
  def loop(remaining: List[A], currentList: List[A], acc: List[List[A]]): List[List[A]] =
    remaining match {
      case a :: as =>
        if (p(a)) loop(remaining = as, currentList = List.empty, currentList.reverse :: acc)
        else loop(remaining = as, a :: currentList, acc)
        
      case Nil =>
        (currentList.reverse :: acc).reverse
    }
  
  loop(remaining = list, currentList = List.empty, acc = List.empty)
}

Which you can use like this:

val myList = List("a", "b", "c", "", "e", "d", "a", "", "d", "a")
val result = splitBy(myList)(_ == "")
// val result: List[List[String]] = List(List(a, b, c), List(e, d, a), List(d, a))

Note that such function is available for the Stream datatype of the fs2.
(Which is probably an overkill for your use case, but it is worth mentioning)

stream.groupAdjacentBy(p).collect {
  case (false, chunk) => chunk
}

You can see the code running here

2

Another option you have, is using foldLeft:

myList.foldRight(List(List[String]()))((s, l) => {
  if (s.isEmpty) {
    List() :: l
  } else {
    (s :: l.head) :: l.tail
  }
})

Code run at Scastie.

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
  • 1
    It should be more efficient to use mutable collection instead – Ivan Stanislavciuc Dec 06 '20 at 15:33
  • This code is not even generic to be reused for different predicates _(but that may be easy to change)_. However, it is simply very inefficient; and, I personally, find it very difficult to understand. – Luis Miguel Mejía Suárez Dec 06 '20 at 15:53
  • @LuisMiguelMejíaSuárez, the same can be achieved with `foldRight` which should be efficient, as I don't add elements to the end of the list. Extracting the condition is fairly easy, if needed. – Tomer Shetah Dec 06 '20 at 16:04
  • @TomerShetah fair, it is only efficient because the stdlib implementation of `foldRight` _"cheat"_ but yeah this is **Scala** not **Haskell**. – Luis Miguel Mejía Suárez Dec 06 '20 at 16:05