Home > Back-end >  Scala: do somthing if get the value in getOrElse
Scala: do somthing if get the value in getOrElse

Time:01-12

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 Somes, 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"))
  • Related