Reputation: 3120
Yesterday I was suddenly enlighted and understood how and why people use 'map' method with Option to compare values. Yes, I'm a bit slow, sorry :)
I revised these very nice links and came to the question I would like to ask.
http://twitter.github.com/effectivescala
http://blog.tmorris.net/posts/scalaoption-cheat-sheet
In my Lift webapp I have some Option[User] and Option[Server] variables. I'm trying to find out if this User is admin of this Server by the following check
if(user.map(_.id) == server.map(_.adminId))
But I noticed that in case of 'user' is None and 'server' is also None this check succeeds which is not good for me (if any of them is None I'd like this check to fail). I could add user.isDefined condition but I feel there is more correct way to do it. Could you tell how to accomplish it in Scala way?
Upvotes: 18
Views: 22217
Reputation: 61656
We can make use of Option#zip
to work with an Option
of the tuple user/server:
user zip server exists { case (user, server) => user.id == server.adminId }
where the behavior of Option#zip
is:
Some(User(id = "hello")) zip Some(Server(adminId = "world"))
// Some((User("hello"), Server("world")))
Some(User(id = "hello")) zip None // None
None zip Some(Server(adminId = "world")) // None
None zip None // None
and where Option#exists
applies a predicate on the optional tuple produced by zip
.
Upvotes: 3
Reputation: 34393
I got used to the combination of exists
/ contains
for this purpose.
When comparing two options of the same type:
o1.exists(o2.contains)
In your case this can be applied using map
:
user.map(_.id).exists(server.map(_.adminId).contains)
Upvotes: 25
Reputation: 7373
you can use a for comprehension
def isAdmin(server: Option[Server])(user: Option[User]): Boolean = (for {
s <- server
u <- user
} yield (u.id == s.adminId)
).getOrElse(false)
The comprehension results in a Option[Boolean]
from which you get
the value or false
if there's no value (the case where any of the options is None
, as you requested)
Why curried?
I made the method curried, so you can define you function for a specific server, and then reuse that to check many users
def isMyServerAdmin = isAdmin(Some(myServer)) _
isMyServerAdmin(Some(user1)) = true
isMyServerAdmin(Some(user2)) = false
isMyServerAdmin(None) = false
Upvotes: 3
Reputation: 32719
You could do it with pattern matching (which in this case is probably the clearest way):
(user, server) match {
case (Some(user), Some(server)) if user.id == server.adminId =>
// both ids are matching, handle this case here
case _ =>
// no match, handle this case here
}
You could also try as a one-liner but here I don't advise it as it's pretty obfuscated:
if ( user.flatMap{ user => server.map(_.adminId == user.id) }.getOrElse( false ) ) {
// both ids are matching, handle this case here
}
else {
// no match, handle this case here
}
Finally, if you only have to handle the case where the ids match (and would just do nothing if there is not), using a for comprehension is not too bad of an option (no pun intended):
for ( user <- user; server <- server if user.id == server.adminId ) {
// both ids are matching, handle this case here
}
Upvotes: 29