Michael
Michael

Reputation: 42100

How to split up an Iterator?

How to split an iterator into a prefix with duplicates and the rest ? For instance,

def splitDupes(it: Iterator[Int]): (Iterator[Int], Iterator[Int]) = ???

val (xs, ys) = splitDupes(List(1, 1, 1, 2, 3, 4, 5).iterator)
xs.toList // List(1, 1, 1)
ys.toList // List(2, 3, 4, 5)

val (xs, ys) = splitDupes(List(1, 2, 3, 4, 5).iterator)
xs.toList // List(1)
ys.toList // List(2, 3, 4, 5)

val (xs, ys) = splitDupes(List(1, 1, 1, 1, 1).iterator)
xs.toList // List(1, 1, 1, 1, 1)
ys.toList // List()

val (xs, ys) = splitDupes(List[Int]().iterator)
xs.toList // List()
ys.toList // List()

Can I use it to read a text file by chunks ?

Upvotes: 1

Views: 491

Answers (2)

Kolmar
Kolmar

Reputation: 14224

You can use the span method to split an Iterable into a prefix that satisfies a predicate and a suffix that doesn't. For Iterators span does the correct thing, and lazily stores elements in the prefix Iterator, in case the suffix was iterated before the prefix has run out.

def splitDupes[T](it: Iterator[T]): (Iterator[T], Iterator[T]) = {
  if (it.isEmpty) (Iterator.empty, Iterator.empty)
  else {
    val head = it.next()
    val (dupes, rest) = it.span(_ == head)
    (Iterator(head) ++ dupes, rest)
  }
}

Example:

scala> val (dupes, rest) = splitDupes(Iterator(1,1,1,2,3,2,1))
dupes: Iterator[Int] = <iterator>
rest: Iterator[Int] = <iterator>

scala> (dupes.toList, rest.toList)
res1: (List[Int], List[Int]) = (List(1, 1, 1),List(2, 3, 2, 1))

Upvotes: 2

What about something like this?
(Note: I decided to return a plain List as the first part since that would already been consumed)

def splitDupes[A](it: Iterator[A]): (List[A], Iterator[A]) = {
  it.nextOption() match {
    case Some(head) =>
      @annotation.tailrec
      def loop(count: Int): (List[A], Iterator[A]) =
        it.nextOption() match {
          case Some(x) if (x == head) =>
            loop(count + 1)

          case Some(x) =>
            List.fill(count)(head) -> Iterator(Iterator.single(x), it).flatten

          case None =>
            List.fill(count)(head) -> Iterator.empty
        }

      loop(count = 1)

    case None =>
      List.empty -> Iterator.empty
  }
}

Upvotes: 1

Related Questions