Matzz
Matzz

Reputation: 692

Scala unFlatMap

I would like to perform something like unFlatMap. Lets say that I have stream:

Stream("|","A","d","a","m","|","J","o","h","n", ...)

Stream could be infinite. I would like to convert it to:

Stream("Adam", "John", ...)

Ofcourse it is only example. In general I would like to perform some operation on elements separated by delimiter, Generic signature would be:

def unFlatMap[B](isSeparator:A => Boolean)(group:Seq[A] => B):TraversableOnce[B]

How to do that in clean and memory efficient way?

Upvotes: 3

Views: 169

Answers (1)

Onilton Maciel
Onilton Maciel

Reputation: 3699

You could do something like this:

def groupStream[A, B](s: Stream[A])(isSeparator: A => Boolean)(group: Seq[A] => B): Stream[B] = 
  group(s.takeWhile(!isSeparator(_)).toList) #:: groupStream(s.dropWhile(!isSeparator(_)).drop(1))(isSeparator)(group)

Or if you want an easier to read but more verbose version:

def groupStream[A, B](s: Stream[A])(isSeparator: A => Boolean)(group: Seq[A] => B): Stream[B] = {
  def isNotSeparator(i: A): Boolean = ! isSeparator(i)

  def doGroupStream(s: Stream[A]): Stream[B] = 
    group(s.takeWhile(isNotSeparator).toList) #:: doGroupStream(s.dropWhile(isNotSeparator).drop(1))

  doGroupStream(s)
}

If you want a implicit method on Stream, you could also do

implicit class ImprovedStream[A](val s: Stream[A]) extends AnyVal {
    def groupStream[B](isSeparator: A => Boolean)(group: Seq[A] => B): Stream[B] = {
      def isNotSeparator(i: A): Boolean = ! isSeparator(i)

      def doGroupStream(st: Stream[A]): Stream[B] = 
        group(st.takeWhile(isNotSeparator).toList) #:: doGroupStream(st.dropWhile(isNotSeparator).drop(1))

      doGroupStream(s)
    }
}

Now, using your example:

val a = Stream("|" ,"A","d","a","m","|","J","o","h","n", "|", "M", "a", "r", "y", "|", "J", "o", "e")

val c = groupStream(a)(_ == "|")(_.mkString)

c.take(10).toList
//  List[String] = List("", Adam, John, Mary, Joe, "", "", "", "", "")

Using the implicit version:

val c = groupStream(a)(_ == "|")(_.mkString)

Upvotes: 4

Related Questions