Let's say I have a Scala list List("apple", "orange", "banana", "chinese gooseberry")*. I want to search this list and return either the previous item in the list in relation to an item I already have.
For example: getPrevious(fruit: String, fruits: List[String]): Option[String] should return
Some("apple") if I call it with a fruit arg of "orange";Some("banana") for "chinese gooseberry";None if I call it with "apple" (no previous element exists) or "potato" (not present in the list).Easily done imperatively, but how can I do this in an elegant functional manner? The best I can come up with is the following:
def previous(fruit: String, fruits: List[String]): Option[String] =
fruits.sliding(2)
.filter { case List(previous, current) => current == fruit }
.toList
.headOption
.map { case List(previous, current) => previous }
It works, but it isn't elegant or efficient. I particularly hate converting the filter iterator toList. How can I improve it?
(*as an aside, is List the best collection to use for a sliding iteration?)
Here's a shorter version using collectFirst:
def previous(fruit: String, fruits: List[String]): Option[String] =
fruits.sliding(2).collectFirst{ case List(previous, `fruit`) => previous}
Note the backticks surrounding fruit to match the parameter value. Using collectFirst will also stop at the first match, rather than running through the entire iterator with filter.
I think this is a case where just straight up recursion and pattern matching is both efficient and easier to read:
@annotation.tailrec
def getPrevious(fruit: String, fruits: List[String]): Option[String] = fruits match {
case Nil => None
case x :: `fruit` :: _ => Some(x)
case _ :: xs => getPrevious(fruit, xs)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With