How to use takeWhile with an Iterator in Scala

iteration, iterator, scala

Solution

With my other answer (which I've left separate as they are largely unrelated), I think you can implement `groupWhen` on `Iterator` as follows:

def groupWhen[A](itr: Iterator[A])(p: (A, A) => Boolean): Iterator[List[A]] = {
  @annotation.tailrec 
  def groupWhen0(acc: Iterator[List[A]], itr: Iterator[A])(p: (A, A) => Boolean): Iterator[List[A]] = {
    val (dup1, dup2) = itr.duplicate
    val pref = ((dup1.sliding(2) takeWhile { case Seq(a1, a2) => p(a1, a2) }).zipWithIndex collect {
      case (seq, 0)       => seq
      case (Seq(_, a), _) => Seq(a)
    }).flatten.toList
    val newAcc = if (pref.isEmpty) acc else acc ++ Iterator(pref)
    if (dup2.nonEmpty)
      groupWhen0(newAcc, dup2 drop (pref.length max 1))(p)
    else newAcc
  }
  groupWhen0(Iterator.empty, itr)(p)
}

When I run it on an example:

println( groupWhen(List(1,1,1,1,3,4,3,2,2,2).iterator)(_ == _).toList )

I get `List(List(1, 1, 1, 1), List(2, 2, 2))`

Problem

I have a Iterator of elements and I want to consume them until a condition is met in the next element, like: ``` val it = List(1,1,1,1,2,2,2).iterator val res1 = it.takeWhile( _ == 1).toList val res2 = it.takeWhile(_ == 2).toList ``` `res1` gives an expected `List(1,1,1,1)` but `res2` returns `List(2,2)` because iterator had to check the element in position 4. I know that the list will be ordered so there is no point in traversing the whole list like `partition` does. I like to finish as soon as the condition is not met. Is there any clever way to do this with Iterators? I can not do a `toList` to the iterator because it comes from a very big file.

Original source