Zwackelmann
Zwackelmann

Reputation: 557

Idiomatic Scala way to traverse Iterator-like data structure

Given a class from a legacy library that behaves like an Iterator such that you can traverse it's content with hasNext and next but it does not implement the Iterator interface:

class LegacyIterator[T](iterable: Iterable[T]) {
  val iterator: Iterator[T] = iterable.iterator
  def hasNext: Boolean = iterator.hasNext
  def next(): T = iterator.next()
}

val lagIter: LegacyIterator[Int] = new LegacyIterator(List(1, 2, 3))

What would be your opinion on the most elegant way to traverse such a data structure - idially inside the scope of the function? I've come up with two versions:

One using @tailrec

@tailrec
def lagIterList(accu: List[Int]): List[Int] =
  if (lagIter.hasNext) lagIterList(lagIter.next() :: accu) else accu

val res = lagIterList(Nil).reverse

And one using LazyList

def ll: LazyList[Int] = if(lagIter.hasNext) lagIter.next() #:: ll else LazyList.empty
val res = ll.toList

What would you think would be more Scala idiomatic? The tailrec version is probably slightly faster on larger data structures but the LazyList version makes better use of the collections library.

Or any other ideas?

Upvotes: 0

Views: 96

Answers (3)

Well, it depends on what you mean by "traverse it". If you only mean, creating a List out of it, I would just use unfold

def toList[T](iter: LegacyIterator[T]): List[T] =
  List.unfold(()) { _ =>
    Option.when(iter.hasNext()) {
      iter.next() -> ()
    }
  }

Upvotes: 1

Dima
Dima

Reputation: 40510

Just convert it into a normal iterator:

val normalIterator = Iterator.continually(legacyIterator)
  .takeWhile(_.hasNext)
  .map(_.next)

Then you can use any of the regular scala tooling to traverse/transform it, e.g.: val list = normalIterator.toList

Upvotes: 2

Lesiak
Lesiak

Reputation: 26084

I would consider an implicit wrapper class extending Iterator[T]:

implicit class LegacyIteratorWrapper[T](private val underlying: LegacyIterator[T]) extends Iterator[T] {
  override def hasNext: Boolean = underlying.hasNext
  override def next(): T = underlying.next()
}

val lagIter: LegacyIterator[Int] = new LegacyIterator(List(1, 2, 3))

// traversal
for (elem <- lagIter) println(elem)

// conversion to list
val lagIterList = lagIter.toList

Upvotes: 2

Related Questions