问题
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)
来源:https://stackoverflow.com/questions/8039261/folding-over-a-list-of-options-to-find-first-or-last-some