0

I have an iterator that contains all the data I need in some sort order. I would like to perform some operation on each data element that takes into account the preceding and subsequent element in the iterator. I.e. I have a function that, given n elements, takes arguments f(iter(k-1), iter(k), iter(k+1)) for 0 < k < n-1.

Of course, I cant just iterate over the iterator because I don't have access to the k + 1 element when I call the function.

I could just cast the whole thing do a list and use indexing, but that would be inelegant. What is the Scala way of accessing these values? Can I somehow compose the iterator with itself and an offset?

4
  • 1
    Scala collections have the sliding method. Iterators should have them too, but I am not sure on this one. Commented Mar 18, 2019 at 5:23
  • You can probably use the zipWithIndex method on list. However, creating the tuples is costly operation and you can use a view instead. Please refer stackoverflow.com/a/6833653/7803797 Commented Mar 18, 2019 at 5:30
  • You can try this approach: stackoverflow.com/questions/53378160/… Commented Mar 18, 2019 at 5:34
  • What is the expected behavior if the iterator has fewer than three elements? Commented Mar 18, 2019 at 6:16

2 Answers 2

3

The comment from @ygor is correct.

yourIterator.sliding(3)
            .collect{ case Seq(prev, current, next) =>
              //proceed accordingly
            }

Note that the code doesn't know which is the final collection-of-3. The last element in yourIterator will never be current.

Sign up to request clarification or add additional context in comments.

1 Comment

This will fail, if number of elements in iterator is less than 3. I burned myself on this one few times.
2

I would consider using method sliding for what you need. Let's say function f returns the same type as the iterator's element type. The following method sliding3Iter will supply a sliding window of 3 elements from a provided Iterator to function f as its arguments:

def sliding3Iter[T](it: Iterator[T], f: (T, T, T) => T): Iterator[T] =
  it.sliding(3).
    collect{ case ls if ls.size == 3 => f(ls(0), ls(1), ls(2)) }

For example:

val f = (i: Int, j: Int, k: Int) => i + j + k

val it = (0 to 10).toIterator

sliding3Iter(it, f).toList
// res1: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27)

1 Comment

I feel, like both answers could be combined to provide a better answer. First, collect is better than map, because it covers iterators with less than 3 elements. Also, collect can directly pattern match on seq of 3 elements, like the other answer does.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.