Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given an element in a sequence, how to get the previous element?

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?)

like image 909
sam-w Avatar asked Jul 31 '14 01:07

sam-w


2 Answers

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.

like image 160
Michael Zajac Avatar answered Oct 07 '22 16:10

Michael Zajac


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)
}
like image 24
stew Avatar answered Oct 07 '22 17:10

stew