Scala: do something if get the value in getOrElse - scala

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 ?

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

Scala copy and reflection

In my project, there are many places where objects are picked out of a collection, copied with some values changed, and pushed back into the collection. I have been trying to create my own 'copy' method, that in addition to making a copy, also gives me a 'Diff' object. In other words, something that contains the arguments you just put into it.
The 'Diff' object should then be sent somewhere to be aggregated, so that someone else can get a report of all the changes since last time, without sending the actual entire object. This is all simple enough if one does it like this:
val user = new User(Some(23), true, "arne", None, None, "position", List(), "email", None, false)
val user0 = user.copy(position = "position2")
list ::= user0
val diff = new Diff[User](Map("position" -> "position2"))
However, there is some duplicate work there, and I would very much like to just have it in one method, like:
val (user, diff) = user.copyAndDiff(position = "position")
I haven't been able to figure out what form the arguments to 'copy' actually takes, but I would be able to work with other forms as well.
I made a method with a Type argument, that should make a copy and a diff. Something like this:
object DiffCopy[Copyable]{
def apply(original:Copyable, changes:Map[String, Any]){
original.copy(??uhm..
original.getAllTheFieldsAndCopyAndOverWriteSomeAccordingToChanges??
My first problem was that there doesn't seem to be any way to guarantee that the original object has a 'copy' method that I can overload to. The second problem appears when I want to actually assign the changes to their correct fields in the new, copied object. I tried to fiddle about with Reflection, and tried to find a way to set the value of a field with a name given as String. In which case I could keep my Diff as a a simple map, and simply create this diff-map first, and then apply it to my objects and also send them to where they needed to go.
However, I ended up deeper and deeper in the rabbit hole, and further and further away from what I actually wanted. I got to a point where I had an array of fields from an arbitrary object, and could get them by name, but I couldn't get it to work for a generic Type. So now I am here to ask if anyone can give me some advice on this situation?
The best answer I could get, would be if someone could tell me a simple way to apply a Map[String, Any] to something equivalent to the 'copy' method. I'm fairly sure this should be possible to implement, but it is simply currently beyond me...
A little bit overcomplicated but solve your original problem.
The best answer I could get, would be if someone could tell me a simple way to apply a Map[String, Any] to something equivalent to the 'copy' method. I'm fairly sure this should be possible to implement, but it is simply currently beyond me...
Take all fields from case class to map.
Update map with new values.
Create case class from new fields map.
Problems:
low performance
I'm pretty sure it can be done simpler...
case class Person(name: String, age: Int)
def getCCParams(cc: Any) =
(Map[String, Any]() /: cc.getClass.getDeclaredFields) {(a, f) =>
f.setAccessible(true)
a + (f.getName -> f.get(cc))
}
def enrichCaseClass[T](cc: T, vals : Map[String, Any])(implicit cmf : ClassManifest[T]) = {
val ctor = cmf.erasure.getConstructors().head
val params = getCCParams(cc.asInstanceOf[Any]) ++ vals
val args = cmf.erasure.getDeclaredFields().map( f => params(f.getName).asInstanceOf[Object] )
ctor.newInstance(args : _*).asInstanceOf[T]
}
val j = Person("Jack", 15)
enrichCaseClass(j, Map("age" -> 18))

How to get object from Play cache (scala)

How to get object from Play cache (scala)
Code to set:
play.api.cache.Cache.set("mykey98", new Product(98), 0)
Code to get:
val product1: Option[Any] = play.api.cache.Cache.get("mykey98")
I get Option object. How to get actual Product object I stored in first step.
First and foremost, I would suggest using Cache.getAs, which takes a type parameter. That way you won't be stuck with Option[Any]. There are a few ways you can do this. In my example, I'll use String, but it will work the same with any other class. My preferred way is by pattern matching:
import play.api.cache.Cache
Cache.set("mykey", "cached string", 0)
val myString:String = Cache.getAs[String]("mykey") match {
case Some(string) => string
case None => SomeOtherClass.getNewString() // or other code to handle an expired key
}
This example is a bit over-simplified for pattern matching, but I think its a nicer method when needing to branch code based on the existence of a key. You could also use Cache.getOrElse:
val myString:String = Cache.getOrElse[String]("mykey") {
SomeOtherClass.getNewString()
}
In your specific case, replace String with Product, then change the code to handle what will happen if the key does not exist (such as setting a default key).

What's the best way to open up a list with 0 or 1 options?

In Scala I have a List with an optional Option. This arises for example when you use for comprehension on a List and your yield returns an Option. In my case I was processing a JSON object and using for comprehension on the list of fields (List[JField]).
What's the best way to open up the list and map List() to None and List(Some(a)) to Some(a)?
A first approach would be
def headOrNone[A](list:List[Option[A]]) =
list match {
case Nil => None
case a::Nil => a
}
Another approach
def headOrNone[A](list:List[Option[A]]) = list.headOption.getOrElse(None)
A third approach (a variation on the headOption implementation)
def headOrNone[A](list:List[Option[A]]) = if (list.isEmpty) None else list.head
I personally prefer the third approach. Is there a better name for this function than headOrNone and what is the idiomatic scala way to write it?
You're solving a problem that probably shouldn't have been created. Instead, you probably want
for (x <- list) yield f(x) // Yields Option
to be
list.flatMap(f)
and then you'll have either zero or one things in your list to begin with (which you can extract using headOption).
How about this:
def headOrNone[A](list: List[Option[A]]) = list.flatten.headOption
headOrNone(List(Some(4))) // Some(4)
headOrNone(List()) // None
Though the first choice has the advantage of giving you an error if you happen to have list with more than one item, which, according to your description, seems like an error condition.
But personally, I would re-evaluate the code that produces the List[Option[A]] and see if there's a way to just have it return the right thing in the first place!

Option monad in scala

how is meant to work Option monad? I'm browsing the scala api and there is an example (I mean the second one),
Because of how for comprehension works, if None is returned from request.getParameter, the entire expression results in None
But when I try this code:
val upper = for {
name <- None //request.getParameter("name")
trimmed <- Some(name.trim)
upper <- Some(trimmed.toUpperCase) if trimmed.length != 0
} yield upper
println(upper.getOrElse(""))
I get a compile error. How is this supposed to work?
You get a compiler error because of this
name <- None
That way, the type of None is set to None.type and the variable name is inferred to be of type Nothing. (That is, it would have this type if it actually existed but obviously the for comprehension does not even get to creating it at runtime.) Therefore no method name.trim exists and it won’t compile.
If you had request.getParameter("name") available, its type would be Option[String], name would potentially have type String and name.trim would compile.
You can work around this by specifying the type of None:
name <- None: Option[String]
To expand on Kevin's answer, you can avoid wrapping values in Some() by using the = operator instead of the <- operator:
val upper = for {
name <- None: Option[String] //request.getParameter("name")
trimmed = name.trim
upper = trimmed.toUpperCase if trimmed nonEmpty
} yield upper
The for-comprehension will compile to something very similar to Kevin's version, but I often find it more clear to use map and filter explicitly to avoid clutter (e.g. extra variable names) that add nothing to the semantic content of the expression.
To expand on Debilski's answer, you also don't need to explicitly wrap subsequent values in Some(), the only value you're actually mapping over is the original name.
A better approach would be be to use the map and filter operations directly instead of a for-comprehension:
NOTE: behind the scenes, the Scala compiler will convert a for-comprehension to a combination of map/flatMap/filter anyway, so this approach will never be less efficient than a for-comprehension, and may well be more efficient
def clean(x:Option[String]) = x map { _.trim.toUpperCase } filterNot { _.isEmpty }
val param = None : Option[String] // request.getParameter("name")
println( clean(param).getOrElse("") )

Scala match/compare enumerations

I have an enumeration that I want to use in pattern matches in an actor. I'm not getting what i'd expect and, now, I'm suspecting I'm missing something simple.
My enumeration,
object Ops extends Enumeration {
val Create = Value("create")
val Delete = Value("delete")
}
Then, I create an Ops from a String:
val op = Ops.valueOf("create")
Inside my match, I have:
case (Ops.Create, ...)
But Ops.Create doesn't seem to equal ops.valueOf("create")
The former is just an atom 'create' and the later is Some(create)
Hopefully, this is enough info for someone to tell me what I'm missing...
Thanks
If you are just trying to get a copy of Create, then you should refer to it directly in your code:
val op = Ops.Create
But if you are parsing it from a string, the string might contain junk, so valueOf returns an Option:
val op1 = Ops.valueOf("create") // Some(Ops.Create)
val op2 = Ops.valueOf("delete") // Some(Ops.Delete)
val op3 = Ops.valueOf("aljeaw") // None
Now, in your match you can just carry along the Option[Ops.Value] and look for:
case(Some(Ops.Create),...)
and you have built-in robustness to junk as input.
Enumeration.valueOf returns None or Some, because you may be asking to create a value that doesn't exist. In your case, for example, Ops.valueOf("blah") would return None, since you don't have an appropriate enumeration value.
To be honest, in this case, I'd use a case class or a case object instead of an Enumeration (they provide better type safety).
It looks like I needed to use the 'get' method of the returned Some to actually get what I wanted. E.g.
ops.valueOf("create").get == Ops.Create
Seems neither intuitive nor friendly but it works.