Is there an elegant way in to update an already existing value in a Map?
This looks too scary:
val a = map.get ( something )
if ( a != null ) // case .. excuse my old language
a.apply( updateFunction )
else
map.put ( something, default )
Most of the time you can insert something that can be updated when it's created (e.g. if it's a count, you put 0 in it and then update to 1 instead of just putting 1 in to start). In that case,
map.getOrElseUpdate(something, default).apply(updateFunction)
In the rare cases where you can't organize things this way,
map(something) = map.get(something).map(updateFunction).getOrElse(default)
(but you have to refer to something twice).
This is what I usually write... not sure if there are better solutions.
map.get(key) match {
case None => map.put(key, defaultValue)
case Some(v) => map(key) = updatedValue
}
In fact update and put are the same for mutable map, but I usually use update on existing entries and put for new ones, just for readability.
Another thing is that if you can figure out what the ultimate value is without checking the existence of the key, you can simply write map(key) = value, at it automatically creates/replaces the entry.
Finally, statements like map(key) += 1 actually works in Map (this is generally true for collections with update function), and so do many simple numeric operations.\
To solve double put, use mutable object instead of immutable values:
class ValueStore(var value: ValueType)
val map = new Map[KeyType, ValueStore]
...
map.get(key) match {
case None => map.put(key, new ValueStore(defaultValue))
case Some(v) => v.value = updatedValue
}
As I mentioned in the comment, the underlying structure of HashMap is HashTable, which actually use this mutable wrapper class approach. HashMap is a nicer wrap-up class but you sometimes just have to do duplicated computation.
me stupid, you are (quite) right:
map.get(key) match {
case None => map.put(key, defaultValue)
case Some(v) => v.apply(updateFunction) // changes state of value
}
tsts
thanks
Related
I have a case class Node that I have written, and create a list from it and I need to find the node that has maximum disk.
I wrote the below code, is there a better way of doing it? Also, in my actual production code, my "nodeList" variable will be not just Option[List[Node]] but Future[Option[List[Node]]]. I guess still the answer/code won't change much except for the fact that I will do a map/flatMap to go inside the future and do the same thing.
If anyone has a better suggestion to write below code more Scala way, please share your thoughts.
scala> case class Node(disk: Integer, name: String)
defined class Node
scala> val nodeList = Option(List(Node(40, "node1"), Node(200, "node3"),Node(60, "node2")))
nodeList: Option[List[Node]] = Some(List(Node(40,node1), Node(200,node3), Node(60,node2)))
scala> val maxDisk = nodeList match {
| case None => println("List is empty"); None
| case Some(lst) => {
| Some(lst.max(Ordering.by((_:Node).disk)))
| }
| }`
maxDisk: Option[Node] = Some(Node(200,node3))
Judging by the code you wrote, I'm not sure if you really should use Optional[List[Node]]. You seem to treat None as an empty List, and you don't check for the empty list in the Some case. You might want to see if just a plain List[Node] suits your use better (where None would become Nil, and Some(lst) is just lst, and the unused Some(Nil) case no longer exists to confuse anyone).
If you do keep Optional[List[Node]], I'd do it like this:
nodeList
.filterNot(_.isEmpty) // maxBy throws if the list is empty; check for it
.map(_.maxBy(_.disk)) // maxBy looks nicer than max(Ordering.by)
If you switch to List[Node], it's slightly uglier:
Some(nodeList)
.filterNot(_.isEmpty) // We're using the filter utility of Option here,
.map(_.maxBy(_.disk)) // so I wrap with Some to get access to filterNot.
You can use recursion with List pattern matching.
case class Node(disk: Integer, name: String)
val nodeList = Option(List(Node(40, "node1"), Node(200, "node3"),Node(60, "node2")))
def findMaxValue(list: List[Node]): Option[Node] = list match {
case Nil => None
case List(x) => Some(x)
case first :: second :: rest => if(first.disk > second.disk) findMaxValue(first::rest) else findMaxValue(second::rest)
}
val node:Option[Node] = findMaxValue(nodeList.getOrElse(Nil))
println(node.get.disk) //print 200
Apologies if this is a newbie question...
In Scala I understand that it is preferred to use an Option rather than returning null when you have a function which returns an instance but could potentially return nothing. I understand that this makes it better with regards to safety, because you are not passing null references around, and risking NullPointerException somewhere down the line.
However, is there a cleaner way to handle options than using pattern matching?
The syntax I end up using is the following:
val optObj : Option[MyObject] = myFunctionThatReturnsOption
optObj match {
case Some(obj) => {
//my code using obj
}
case None => _
}
In reality all this doing is the equivalent of the Java version:
MyObject obj = myMethodThatCanReturnNull()
if (obj != null) {
//my code using obj
}
Is there some other way to avoid all this boilerplate in Scala when using Option instead of null references? All I want to do is execute a piece of code as long as the Option contains some object (i.e. is not None).
Use foreach, getOrElse and/or map if you want to work in a more consistent way. Here's some use cases and what I'd do:
//I want to get a non-null value and I have a sane default
val result = myOption getOrElse 3
//I want to perform some side effecting action but only if not None
myOption foreach{ value =>
println(value toString ())
}
//equivalently
for(value <- myOption){
//notice I haven't used the "yeild" keyword here
}
//I want to do a computation and I don't mind if it comes back as an Option
val result = for(value <- myOption) yield func(value)
val equivalent = myOption map func
The third example will use map in both cases.
It gets really interesting when you can mix and match things in a "for comprehension" (Google term.) Let's say that func also returns an Option but I only want things working in specific cases:
val result = for{
value <- myOption if value > 0
output <- func(value)
} yield output
Now I get back an Option but only if myOption contained an integer that was greater than zero. Pretty nifty stuff, no?
You can use foreach if you just want to perform some side-effecting operation with the value:
optObj.foreach(obj => {
//my code using obj
})
if you have some other use case you should use some other method on Option like map, filter or getOrElse.
Of course, the way I usually use options if I only care about present value is foreach:
optObj.foreach { obj =>
//...
}
Having said this, there are a lot of other options (which #wheaties enlisted) and some people keep battling about the true one.
You can use the flatMap-method pretty well with Option. Like hier:
case class Player(name: String)
def lookupPlayer(id: Int): Option[Player] = {
if (id == 1) Some(new Player("Sean"))
else if(id == 2) Some(new Player("Greg"))
else None
}
def lookupScore(player: Player): Option[Int] = {
if (player.name == "Sean") Some(1000000) else None
}
println(lookupPlayer(1).map(lookupScore)) // Some(Some(1000000))
println(lookupPlayer(2).map(lookupScore)) // Some(None)
println(lookupPlayer(3).map(lookupScore)) // None
println(lookupPlayer(1).flatMap(lookupScore)) // Some(1000000)
println(lookupPlayer(2).flatMap(lookupScore)) // None
println(lookupPlayer(3).flatMap(lookupScore)) // None
Here's a great reference for Scala best practices regarding options:
http://blog.tmorris.net/posts/scalaoption-cheat-sheet/index.html
What is the more idiomatic way to handle an Option, map / getOrElse, or match?
val x = option map {
value => Math.cos(value) + Math.sin(value)
} getOrElse {
.5
}
or
val x = option match {
case Some(value) => Math.cos(value) + Math.sin(value)
case None => .5
}
You could always just look at the Scaladoc for Option:
The most idiomatic way to use an scala.Option instance is to treat it as a collection or monad and use map,flatMap, filter, or foreach:
val name: Option[String] = request getParameter "name"
val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
println(upper getOrElse "")
And a bit later:
A less-idiomatic way to use scala.Option values is via pattern matching:
val nameMaybe = request getParameter "name"
nameMaybe match {
case Some(name) =>
println(name.trim.toUppercase)
case None =>
println("No name value")
}
Use fold for this kind of map-or-else-default thing:
val x = option.fold(0.5){ value => Math.cos(value) + Math.sin(value) }
Obviously both are valid and I don't think one is more idiomatic than the other. That being said, using map uses the fact the Option is a Monad. This can be particularly advantageous when combining two Options. Say you have two Option[Int] that you would like to add. In this case instead of doing multiple matches it is much cleaner to use map/flatMap and it's equivalent "for comprehensions". So for your example both are valid... but for other examples using map/flatMap is often much more succinct.
Some(6).flatMap(intValue => Some(5).map(intValue + _))
or
for {
i <- Some(6)
j <- Some(5)
} yield i + j
All of them have different semantics, so in your case none of them.
map applies some function to the value inside Option, if it exists (Some, not None). Basically this is how you safely work with Options, appling function on some null value is dangeroues, cause it can throw NPE, but in case with Option it just returns None.
getOrElse simply returns either it's value or default one (which you provide as an argument). It won't do anything with the value inside the Option, you can just extract it, if you have Some, or return a default one, in case of None.
and match approach i'd say is a combination of two, cause you can apply some computation on the values and extract it from the Option
(NOTE I'm quit new to Scala and still struggle with most common operations of collection manipulation.)
I would like to convert a List[Task] into a Map. Here's some details:
// assignee may be null
case class Task(assignee: String, description: String)
// might refactor it into:
// case class Task(assignee: Option[String], description: String)
I want a Map where Keys are the assignees and each Value is a Set[Task]. I'm having trouble managing the following two situations:
Map's not being (cough) friendly (cough) with null Keys (I worked around this one using Option[String] for assignee) and
having to distinguish whether a Key already exists in the map (only add value to existing set) vs key already added so the Set value exists
I came up with the following but it looks overly verbose.
def groupByAssignee(tasks : List[Task]) : Map[Option[String], Set[Task]] = {
tasks.foldLeft(Map[Option[String], Set[Task]]())(
(m, t) => {
m.get(t.assignee) match {
case Some(_) => m + ((t.assignee, m.get(t.assignee).get.+(t)))
case _ => m + ((t.assignee, Set(t)))
}
})
}
What's a easier/clearer way to achieve this?
Thanks!
This use case is so common that there is a built-in method for that:
tasks groupBy {_.assignee}
groupBy however will return Map[String,List[Task]] while you want .Map[String, Set[String]]. This should do it:
groupBy {_.assignee} mapValues {_ map {_.description} toSet}
groupBY is null-friendly, but you shouldn't be. Option[String] is much better and more idiomatic.
I'm splitting an input of type Option[String] into an Option[Array[String]] as follows:
val input:Option[String] = Option("a=b,1000,what?")
val result: Option[Array[String]] = input map { _.split(",") }
I want to add a test whereby if any member of the array matches (eg, is an Long less than 0), the whole array is discarded and an empty Option returned.
Use filter to perform a test on the content of an Option.
Use exists to check whether any member of the collection fullfils a condition.
result.filter(! _.exists(s => test(s)))
or
result.filterNot(_.exists(s => test(s)))
Have you considered using find() on the collection ? If it returns a Some(x), then something has satisfied the condition.
list.find(_ < 0) match {
case Some(x) => None
case None => Some(list)
}
Of course you know that you can split and then filter as #ziggystar suggests, but if you have a really big Stringand an element at the beginning matches then it's pointless to finish splitting the string when you know it's going to be discarded.
In this case, if you're worried about time efficiency, you can use a Stream and re-implement the split operation, something like this:
def result(input:Option[String]):Option[Seq[String]] = {
def split(c: Char, chars:Stream[Char]):Stream[String] = {
val (head,tail) = chars span(_ != c)
head.mkString #:: (if(tail isEmpty) Stream.empty else split(c, tail tail))
}
input map {s => split(',', Stream(s:_*)) } filter (_.forall (s => !test(s)))
}
Note that the map/filter structure stays the same, but it is now short-circuiting due to the use of Stream.
If it's a really big string you probably have it as a Stream[Char] already which means you don't even have the memory overhead of hanging on the original String.