2

Suppose I've got class A with method foo:

class A(x: Int) { def foo(): Option[Int] = if (x > 0) Some(x) else None }

Now I am writing function bar like this:

def bar(as: List[A]): Option[(A,  Int)] = for {
  a <- as.view.find(_.foo.nonEmpty)
  foo <- a.foo
} yield (a, foo)

The code above works fine but what it invokes the foo method twice for the found A instance. How to re-write bar to invoke method foo only once for the found A instance ?

Artemis
  • 2,553
  • 7
  • 21
  • 36
Michael
  • 41,026
  • 70
  • 193
  • 341

2 Answers2

1
 as.iterator.flatMap { a => a.foo.map { a -> _ } }.find(_ => true)
Dima
  • 39,570
  • 6
  • 44
  • 70
  • Does it always iterate over the _whole_ list instead of stopping when it finds the required instance ? – Michael Dec 05 '17 at 11:57
  • no, it doesn't. Iterators are lazy, like views, just less ... weird. – Dima Dec 05 '17 at 11:58
  • Does it compile ? Looks like `Iterator` does not have method `headOption`. – Michael Dec 05 '17 at 12:01
  • I got `:15: error: value headOption is not a member of Iterator[(A, Int)]` – Michael Dec 05 '17 at 12:01
  • Oops ... indeed. That's weird. Ok, how about now? – Dima Dec 05 '17 at 12:03
  • There are two solutions in the comment :) They all work to your requirements, so, I am not sure which one is "better" , and how you define the criteria to figuring it out. Personally, I am not a fun of views and (to a lesser extent) of for-comprehensions (and also constructs like `_._2` make me cringe), and explicit checks for `Option.nonEmpty` aren't very idiomatic, so, I prefer this one :) Using (lazy) val instead of `def` seems fine too ... but that one you don't like for some reason, so .. – Dima Dec 05 '17 at 12:10
  • I meant the solution of @Bergi which, as it's turns out, does not compile. – Michael Dec 05 '17 at 12:13
  • 1
    BTW I am ok with `view`, for-comprehension, `_._2`, and all that scala stuff. – Michael Dec 05 '17 at 12:14
  • Well ... good for you :D I am "ok" with it too ... It's just error prone and not very readable. – Dima Dec 05 '17 at 12:15
0

Use streams. The elements in stream are evaluated lazily.

def bar(as: List[A]): Option[(A, Int)] = {
  as
    .toStream
    .map(x => (x, x.foo()))
    .find(z => z._2.isDefined)
    .map(p => (p._1, p._2.get))
}
Sujit Kamthe
  • 811
  • 11
  • 14