0

I have a map which lays down prices of commodities in different currencies

val commpricemap: Map[String , Map[String, Double]] = ???

AN example of an entry for gold is as below:

("AU" -> ( "USD" -> 1024))

The code to extract the value is below :

commpricemap.get("AU") match {
  case Some(m) if m.contains("USD") =>  
    m.get("USD").get  
  case _ =>  
    defaultfallbackrate  
}

The drawback is the contains and get - avoidable inefficiency in the case of voluminous data. How can I avoid it

2 Answers 2

3

Note that if you're sure the map contains the key already, you can just use apply (or just treat the map as a function):

commpricemap.get("AU") match {
  case Some(prices) if prices.contains("USD") =>
    prices("USD")

  case _ => 0.0
}

Performance-wise (benchmark to make sure!) if-contains-then-apply will avoid allocations (though it's reasonably likely that your JVM will, if this is a hot code path, optimize the allocation heavy versions into something almost exactly like if-contains-then-apply).

commpricemap.get("AU").flatMap(_.get("USD")).getOrElse(0.0)

...is arguably optimizing for concision, and assuming reasonably aggressive inlining and on-stack-replacement, will get to the same code at runtime in the end.

getOrElse does incur the cost of wrapping the default value in a lambda ("thunk"), even in the defined case, so

commprice.get("AU").flatMap(_.get("USD") match {
  case Some(result) => result
  case None => defaultfallbackrate
}

avoids that allocation if the expectation is that most of the time there's a price for the commodity in the currency of interest (though again, when it ultimately gets optimized into ifs, the allocation will be delayed).

Sign up to request clarification or add additional context in comments.

4 Comments

Would the compiler really go through the effort of creating a lambda for a constant default value? It feels like the sort of thing that would be optimised away.
AFAIK, the JVM signature for getOrElse (as it has a by-name parameter) requires that a lambda be passed in order to pass bytecode verification (since the JVM has no native construct for by-name). Conceivably for at least some statically-known constants, the compiler could statically define a lambda ahead of time that returns that constant.
The source code is browsable from my IDE so I assumed that it would be visible to the compiler which would then "inline" it. JIT will, of course, do this, but feels like it could be done earlier.
Could be done, but isn't. Part of this is because the library version can change between compile time and runtime, some is that there's not really demand (though I'd expect that ScalaJS and Native are more aggressive here), and some is that a lot of the library isn't written in a way that's amenable to AOT inlining without special-casing the library in the compiler.
2

You could use getOrElse like this:

val price = commpricemap.get("AU") match {
  case Some(m) => m.getOrElse("USD", defaultfallbackrate)
  case _ => defaultfallbackrate   
}

Note that defaultfallbackrate is guaranteed to only be evaluated once, because the default-value parameter to getOrElse is lazy.

Edit: You could also use a for-comprehension like this. That will avoid the duplicated defaultfallbackrate.

val price = (for {
  gold <- commpricemap.get("AU")
  usd <- gold.get("USD")
} yield usd) getOrElse defaultfallbackrate

4 Comments

have modified the question - the default is a fallback function. With getorelse the fallback function call will be duplicated
@IUnknown The fallback will only be called once. The defaultValue parameter to getOrElse is lazy.
i meant duplication from a coding convention perspective
@IUnknown See edit. A for-comprehension will avoid that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.