質問

I have an iterator of Options, and would like to find the first member that is:

  1. Some
  2. and meets a predicate

What's the best idiomatic way to do this?

Also: If an exception is thrown along the way, I'd like to ignore it and move on to the next member

役に立ちましたか?

解決

optionIterator find { case Some(x) if predicate(x) => true  case _ => false }

As for ignoring exceptions… Is it the predicate that could throw? 'Cause that's not really wise. Nonetheless…

optionIterator find {
  case Some(x) => Try(predicate(x)) getOrElse false
  case _       => false
}

他のヒント

Adding a coat of best and idiomatic to the paint job:

scala> val vs = (0 to 10) map { case 3 => None case i => Some(i) }
vs: scala.collection.immutable.IndexedSeq[Option[Int]] = Vector(Some(0), Some(1), Some(2), None, Some(4), Some(5), Some(6), Some(7), Some(8), Some(9), Some(10))

scala> def p(i: Int) = if (i % 2 == 0) i > 5 else ???
p: (i: Int)Boolean

scala> import util._
import util._

scala> val it = vs.iterator
it: Iterator[Option[Int]] = non-empty iterator

scala> it collectFirst { case Some(i) if Try(p(i)) getOrElse false => i }
res2: Option[Int] = Some(6)

Getting the first even number over five that doesn't blow up the test.

Assuming that you can wrap your predicate so that any error returns false:

iterator.flatMap(x => x).find(yourSafePredicate)

flatMap takes a collection of collections (which an iterable of Option is as Option and Either are considered collections with a max size of one) and transforms it into a single collection:

scala> for { x <- 1 to 3; y <- 1 to x } yield x :: y :: Nil
res30: IndexedSeq[List[Int]] = Vector(List(1, 1), List(2, 1), List(2, 2), List(3, 1), List(3, 2), List(3, 3))

scala> res30.flatMap(x => x)
res31: IndexedSeq[Int] = Vector(1, 1, 2, 1, 2, 2, 3, 1, 3, 2, 3, 3)

find returns the first entry in your iterable that matches a predicate as an Option or None if there is no match:

scala> (1 to 10).find(_ > 3)
res0: Option[Int] = Some(4)

scala> (1 to 10).find(_ == 11)
res1: Option[Int] = None

Some sample data

scala> val l = Seq(Some(1),None,Some(-7),Some(8))
l: Seq[Option[Int]] = List(Some(1), None, Some(-7), Some(8))

Using flatMap on a Seq of Options will produce a Seq of defined values, all the None's will be discarded

scala> l.flatMap(a => a)
res0: Seq[Int] = List(1, -7, 8)

Then use find on the sequence - you will get the first value, that satisfies the predicate. Pay attention, that found value is wrapped as Option, cause find should be able to return valid value (None) value in case of "not found" situation.

scala> l.flatMap(a => a).find(_ < 0)
res1: Option[Int] = Some(-7)

As far as I know it is "OK" way for the Scala.

Might be more idiomatic way is to use collect / collectFirst on the Seq ...

scala> l.collectFirst { case a@Some(x) if x < 0 => a }
res2: Option[Some[Int]] = Some(Some(-7))

Pay attention that here we have Some(Some(-7)) because the collectFind should have chance to produce "not found" value, so here 1st Some - from collectFirst, the 2nd Some - from the source elements of Seq of Option's.

You can flatten the Some(Some(-7)) if you need the values in your hand.

scala> l.collectFirst({ case a@Some(x) if x < 0 => a }).flatten
res3: Option[Int] = Some(-7)

If nothing found - you will have the None

scala> l.collectFirst({ case a@Some(x) if x < -10 => a }).flatten
res9: Option[Int] = None
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top