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?

Logister
  • 1,852
  • 23
  • 26
  • 1
    Scala collections have the `sliding` method. Iterators should have them too, but I am not sure on this one. – ygor Mar 18 '19 at 05: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 https://stackoverflow.com/a/6833653/7803797 – Chaitanya Mar 18 '19 at 05:30
  • You can try this approach: https://stackoverflow.com/questions/53378160/scala-function-which-removes-elements-out-of-a-list-with-a-bigger-predecessor/53378379#53378379 – Bogdan Vakulenko Mar 18 '19 at 05:34
  • What is the expected behavior if the iterator has fewer than three elements? – Brian McCutchon Mar 18 '19 at 06:16

2 Answers2

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.

jwvh
  • 50,871
  • 7
  • 38
  • 64
  • This will fail, if number of elements in iterator is less than 3. I burned myself on this one few times. – ygor Mar 18 '19 at 06:15
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)
Leo C
  • 22,006
  • 3
  • 26
  • 39
  • 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. – ygor Mar 18 '19 at 06:18