Reputation: 9604
I have problems with writing an specific application in a scala-esque and elegant way. I tried this for some time now, but I cannot find a "good" solution to this Problem:
Given that I have the following List:
List("foo", "bar", "baz", "blah")
I want to Iterate over this list, not only giving me the current element for each Iteration but also the element before and after the current element. This might be a Tuple3 but is not required to. This could be the Tuple signature:
(Option[T], T, Option[T])
To clarify what i mean, this is the proposed Tuple for each iteration over a List[String]
, ending after the fourth.
Iteration 1: (None, "foo", Some("bar"))
Iteration 2: (Some("foo"), "bar", Some("baz"))
Iteration 3: (Some("bar"), "baz", Some("blah"))
Iteration 4: (Some("baz"), "blah", None)
How could I achieve such a result? Again: I am not bound to the Tuple3, any other solution is also very appreciated!
Thanks!
Upvotes: 11
Views: 7316
Reputation: 297205
Better use Scala 2.8 and retronym's solution, of course, but here is my solution for Scala 2.7:
class MyIterator[T](l: List[T]) extends Iterator[(Option[T],T,Option[T])] {
var last: Option[T] = None
var curr = l
def hasNext = !curr.isEmpty
def next = {
val t = curr match {
case first :: second :: tail => (last, first, Some(second))
case first :: Nil => (last, first, None)
case Nil => throw new java.util.NoSuchElementException
}
last = Some(curr.head)
curr = curr.tail
t
}
}
Upvotes: 2
Reputation: 55028
Here's one approach. It uses a new Scala 2.8 collection method sliding
.
def window[A](l: List[A]): Iterator[List[Option[A]]] =
(None :: l.map(Some(_)) ::: List(None)) sliding 3
window(List(1, 2, 3, 4, 5)).toList
// List(List(None, Some(1), Some(2)), List(Some(1), Some(2), Some(3)), List(Some(2), Some(3), Some(4)), List(Some(3), Some(4), Some(5)), List(Some(4), Some(5), None))
Update: Heres a version that works for Streams.
def windowS[A](s: Stream[A]): Stream[List[Option[A]]] =
(None #:: s.map(Some(_): Option[A]) #::: Stream(None: Option[A])).sliding(3).toStream.map(_.toList)
val posInts = Stream.range(1, Integer.MAX_VALUE)
windowS(posInts).take(5).toList
Upvotes: 16
Reputation: 167891
Retronym's answer works well if you're using 2.8. If you're using 2.7.x, there isn't a great stock solution, but you can build your own easily. For example, if you want only triples where before and after exist, you can do something like this:
class Tuple3Iterator[T](solo: Iterator[T]) extends Iterator[(T,T,T)] {
var current = if (solo.hasNext) Some(solo.next) else None
var future = if (solo.hasNext) Some(solo.next) else None
def hasNext = solo.hasNext
def next = {
val past = current
current = future
future = Some(solo.next)
(past.get,current.get,future.get)
}
}
class IteratorToT3[T](it: Iterator[T]) {
def treble = new Tuple3Iterator[T](it)
}
implicit def allowTrebling[T](it: Iterable[T]) = new IteratorToT3[T](it.elements)
scala> List("Hi","there",5,"you").treble.foreach(println(_))
(Hi,there,5)
(there,5,you)
If you prefer to allow before and after to stay Options, (edit: I didn't really give a complete or error-free set of changes before) then instead use
class Tuple3Iterator[T](solo: Iterator[T]) extends Iterator[(Option[T],T,Option[T])] {
var current = None:Option[T]
var future = if (solo.hasNext) Some(solo.next) else None
def hasNext = (solo.hasNext || future!=None)
def next = {
val past = current
current = future
future = if (solo.hasNext) Some(solo.next) else None
(past,current.get,future)
}
}
scala> List("Hi","there",5,"you").treble.foreach(println(_))
(None,Hi,Some(there))
(Some(Hi),there,Some(5))
(Some(there),5,Some(you))
(Some(5),you,None)
Upvotes: 3