Folding over a list of Options to Find First or La

2019-04-07 10:39发布

问题:

I'm trying to fold over a list of Options in order return the first(or last) Some value or None if there aren't any Some values.

scala> val opts = List(None, Some(1), None, Some(2), None)
opts: List[Option[Int]] = List(None, Some(1), None, Some(2), None)

scala> opts foldLeft(None)((a,io) => a match { case None => io; case Some(i) =>
a})
<console>:9: error: object None does not take parameters
              opts foldLeft(None)((a,io) => a match { case None => io; case Some
(i) => a})
                                 ^

Not sure what I'm doing wrong. Also there is probably a way to do this simpler using a higher order function but nothing from here caught my eye.

回答1:

Maybe this can solve your problem - the first element:

opts.flatten.headOption

And the last element:

opts.flatten.lastOption

flatten method will unbox all Option values in the list and drop all None values. headOption/lastOption will return either Some for the first/last element in the list or None if list is empty.



回答2:

tenshi’s answer is pretty straightforward but for long lists it will attempt to flatten everything as it isn’t lazy. (I think view won’t help us here either but I’m not quite sure.)

In that case, you could use:

opts.dropWhile(_.isEmpty).headOption.flatMap(identity)

Unfortunately, we cannot use flatten here as this will return a generic Iterable[Int] and no Option, so we have to chose the longer idiom flatMap(identity).

Edit: As dave noticed:

opts.find(_.isDefined).flatMap(identity)

would be even better.



回答3:

There are better ways to do it, but to answer the question as posed, you have two problems

1) You are missing the . following opts. You can only use infix notation to convert a.m(b) to a m b. The foldLeft method is of the form a.m(b)(c). So either write it like that, or include parentheses (a m b)(c).

2) You need to parameterize None as an Option[Int]: it's being interpreted here as the None object, rather than the value of an Option[Int] instance.

So this will work:

opts.foldLeft(None: Option[Int])(
  (a,io) => a match { case None => io; case Some(i) => a } )


回答4:

Why go to such trouble?

opts.find(_.nonEmpty).flatten
opts.reverse.find(_.nonEmpty).flatten


回答5:

Starting Scala 2.9, you can use collectFirst to extract the first defined option:

List(None, Some(1), None, Some(2), None).collectFirst { case Some(x) => x }
// Option[Int] = Some(1)

this will stop iterating at the first defined Option and avoids flattening the whole List before looking for its head.


Starting Scala 2.13 you can use findLast, which is the reverse of find (while waiting for a possible collectLast?)

List(None, Some(1), None, Some(2), None).findLast(_.isDefined).flatten
// Option[Int] = Some(2)


标签: scala folding