If a variable is an Option[Account]
, and there is a string field called accountName
in the class Account
.
e.g:
val allAccounts: Set[Option[Account]] = Set(Some(Account1), Some(Account2), None)
How do I get the accountName
from Some(Account)
if I get something from getOrElse?
I tried allAccounts.map(_.getOrElse("").accountName)
but it doesn't work. It cannot apply to the "get" part but the "OrElse" part
Thanks for your help!
PS: wonder why allAccounts.map(_.map(_.accountName).getOrElse(""))
works fine with None value but if I create another variable: val sampleAccount2 = None
and sampleAccount2.map(_.accountName).getOrElse("")
will failed? Basically I just goes from Set(None)
to None
?
CodePudding user response:
Is this what you ultimately wanted to achieve?
final case class Account(accountName: String)
val allAccounts: Set[Option[Account]] =
Set(Some(Account("Account1")), Some(Account("Account2")), None)
def getAccountNames(maybeAccounts: Set[Option[Account]]): Set[String] =
maybeAccounts.map(_.fold("")(_.accountName))
assert(getAccountNames(allAccounts) == Set("Account1", "Account2", ""))
You can play around with this code here on Scastie.
Another way to write getAccountNames
is by using a combination of map
and getOrElse
instead of fold
, like so:
def getAccountNames(maybeAccounts: Set[Option[Account]]): Set[String] =
maybeAccounts.map(_.map(_.accountName).getOrElse(""))
This is probably closer to what you initially wanted to write. In this case fold
and map
with getOrElse
are basically equivalent, choose whichever makes more sense given your knowledge of the code base you're working on at the moment.
This version is also available here on Scastie.
The problem with your attempt if that you were applying getOrElse
to the Option[Account]
type, meaning that you were trying to return something that was either an Account
(within the Option
) or a String
and from that thing you were then asking the accountName
, which only makes sense on Account
but not on String
. The key difference is that in this case you first map
on Option[Account]
to get the accountName
on Some
s, getting an Option[String]
, and then you either get what's in there or the default value if the Option
is empty.
As further input, please note that since you are using a Set
, if you have multiple empty values in your input, they will be effectively collapsed into one, as in the following example:
assert(getAccountNames(Set(None, None)) == Set(""))
If by any chance you would rather remove any empty value entirely from the output, you can do so by rewriting the function above so that it's defined like so (Scastie):
def getAccountNames(maybeAccounts: Set[Option[Account]]): Set[String] =
maybeAccounts.flatMap(_.map(_.accountName))
In this case getAccountNames
can be redefined in terms of a for
-comprehension (more on the topic here on the Scala documentation):
def getAccountNames(maybeAccounts: Set[Option[Account]]): Set[String] =
for {
maybeAccount <- maybeAccounts
account <- maybeAccount
} yield account.accountName
This last example is also available here on Scastie for you to play around with it.
In both cases, the assertion that holds now changes to the following:
assert(getAccountNames(allAccounts) == Set("Account1", "Account2"))