I create an ObservableMap, and a subscriber that just prints any events it receives (taken from here):
class MyMap extends HashMap[Int,Int] with ObservableMap[Int,Int]
class MySub extends Subscriber[Message[(Int,Int)] with Undoable, ObservableMap[Int, Int]] {
def notify(pub: ObservableMap[Int, Int], evt: Message[(Int, Int)] with Undoable) {
println(evt)
}
}
val map = new MyMap
map.subscribe(new MySub)
Using +=, ++=, and -= work as expected:
scala> map += 1 -> 1
Include(NoLo,(1,1))
res5: map.type = Map(1 -> 1)
scala> map ++= Map(2 -> 4, 3 -> 9)
Include(NoLo,(3,9))
Include(NoLo,(2,4))
res6: map.type = Map(3 -> 9, 1 -> 1, 2 -> 4)
scala> map -= 1
Remove(NoLo,(1,1))
res7: map.type = Map(3 -> 9, 2 -> 4)
But update doesn't work:
scala> map(4) = 16
scala> map
res9: MyMap = Map(3 -> 9, 4 -> 16, 2 -> 4)
Why? It looks like ObservableMap overrides +=, -=, and clear. Both ++= and update appear to be implemented in terms of += (by Growable and MapLike respectably), so why does it work on one but not the other?
The mutable version of HashMap calls update which in turn calls put, which does not call += so the observable += method doesn't get called. I'm using scala 2.9.1, but this should be the same for 2.8 on.
From HashMap:
override def put(key: A, value: B): Option[B] = {
val e = findEntry(key)
if (e == null) { addEntry(new Entry(key, value)); None }
else { val v = e.value; e.value = value; Some(v) }
}
override def update(key: A, value: B): Unit = put(key, value)
def += (kv: (A, B)): this.type = {
val e = findEntry(kv._1)
if (e == null) addEntry(new Entry(kv._1, kv._2))
else e.value = kv._2
this
}
Related
Sometimes I use a Map as a memoization cache. With mutable maps, I use getOrElseUpdate:
mutableMap.getOrElseUpdate(key, {
val value = <compute the value>
value
})
Immutable maps don't have getOrElseUpdate. So I want to do this
immutableMap.getOrElse(key, {
val value = <compute the value>
immutableMap += key -> value
value
})
This seems to work in practice, I have good arguments to believe it works in theory, and it's more or less readable -- is it a terrible idea for some reason I'm missing?
The other alternatives I'm considering are
immutableMap.get(key) match {
case Some(value) => value
case None =>
val value = <compute the value>
immutableMap += key -> value
value
}
which is not much different and is more cumbersome, or
if (immutableMap.contains(key)) {
immutableMap(key)
} else {
val value = <compute the value>
immutableMap += key -> value
value
}
which is the dumbest and probably least idiomatic.
In principle I rather not go for a solution that uses a helper to return the value and the updated map, unless it's the unarguably superior way.
Sure, it seems reasonable except for one small issue... it's not updating your collection! If you're using an immutable Map, then that Map is immutable. You can not change it, ever.
In fact, immutable Map from Scala collection does not even have a += method defined on it, see immutable.Map. All the methods with "append" or "add" new values to the Map actually return a new Map. So for what you've written above to compile, you'd have to not be using something immutable.
To do this with an immutable map, you'll need to work with a var and replace that var with the new Map (which can lead to issues with threading) or you have to adopt a State Monad type pattern in which you return not only the new value but also the new Map.
def getOrCalc(m: Map[Key, Value], k: Key)(f: Key => Value): (Map[Key, Value], Value] ={
if(m.contains(k)) (m, m(k))
else{
val value = f(k)
(m +: (k, value), value)
}
}
My only recommendation (regarding the reasons why you choosed var instead of mutable.Map or Java's ConcurrentMap) is to wrap it into DSL, like:
case class Mutable[K,V](var m: Map[K,V]) {
def orElseUpdate(key: K, compute: => V) = m.getOrElse(key, {
val value = compute
m += key -> value
value
})
}
scala> val a = Mutable(Map(1 -> 2))
a: Mutable[Int,Int] = Mutable(Map(1 -> 2))
scala> a.orElseUpdate(2, 4)
res10: Int = 4
scala> a.orElseUpdate(2, 6)
res11: Int = 4
scala> a.orElseUpdate(3, 6)
res12: Int = 6
Another option (if your computation is lightweight) is just:
m += key -> m.getOrElse(key, compute)
m(key)
Example:
scala> var m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> m += 3 -> m.getOrElse(3, 5)
scala> m
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += 3 -> m.getOrElse(3, 5)
scala> m
res3: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += 3 -> m.getOrElse(3, 6)
scala> m
res5: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
You can wrap it into DSL as well:
implicit class RichMap[K,V](m: Map[K,V]) {
def kvOrElse(k: K, v: V) = k -> m.getOrElse(k, v)
}
scala> m += m.kvOrElse(3, 7)
scala> m
res7: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5)
scala> m += m.kvOrElse(4, 7)
scala> m
res9: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 5, 4 -> 7)
Is there a way to define a Map, where Map value depends on its key, like
Map(key -> f(key), key2 -> f(key2), ...).
You're looking at this the wrong way...
A Map[K,V] is also an instance of Partialfunction[K,V]. So change everywhere you're using your Map type (vals, method params, etc.) to be a PartialFunction.
Then you can just work with f directly, or supply a Map[K,V] as the instance wherever you don't have a simple algebraic relationship between keys and values.
e.g.
def methodUsingMapping(x: PartialFunction[Int,Boolean]) = ...
//then
val myMap = Map(1->true, 2->true, 3->false)
methodUsingMapping(myMap)
//or
val isEven = PartialFunction(n: Int => n % 2 == 0)
methodUsingMapping(isEven)
//or
//note: case statements in a block is the smart way
// to define a partial function
// In this version, the result isn't even defined for odd numbers
val isEven: PartialFunction[Int,Boolean] = {
case n: Int if n % 2 == 0 => true
}
methodUsingMapping(isEven)
You also might also want to consider using (K) => Option[V], in which case you can supply an instance of the type via the lift method, which maps inherit from PartialFunction
e.g.
def methodUsingMapping(x: (Int)=>Option[Boolean]) = ...
//then
val myMap = Map(1->true, 2->true, 3->false)
methodUsingMapping(myMap.lift)
//or
def isEven(n: Int) = Some(n % 2 == 0)
methodUsingMapping(isEven)
//or
def isEven(n: Int) = n % 2 == 0
methodUsingMapping(x => Some(isEven(x)))
Lets say you have your keys in a list like this, and you want to convert it map with squares as values.
scala> val keyList = ( 1 to 10 ).toList
keyList: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> val doSquare = ( x: Int ) => x * x
doSquare: Int => Int = <function1>
// Convert it to the list of tuples - ( key, doSquare( key ) )
scala> val tupleList = keyList.map( key => ( key, doSquare( key ) ) )
tuple: List[(Int, Int)] = List((1,1), (2,4), (3,9), (4,16), (5,25), (6,36), (7,49), (8,64), (9,81), (10,100))
val keyMap = tuple.toMap
keyMap: scala.collection.immutable.Map[Int,Int] = Map(5 -> 25, 10 -> 100, 1 -> 1, 6 -> 36, 9 -> 81, 2 -> 4, 7 -> 49, 3 -> 9, 8 -> 64, 4 -> 16)
Or to do it in one line
( 1 to 10 ).toList.map( x => ( x, x * x ) ).toMap
Or... if you have just few keys... then you can write the specific code
Map( 1 -> doSquare( 1 ), 2 -> doSquare( 2 ) )
Because you only need to define 4 methods to make a Map trait implementation, you could just roll your own:
trait MapWithRelationship[K, +V] extends Map[K, V] {
self =>
def pred: (K, Any) => Boolean
def underlying: Map[K, V]
def get(key: K): Option[V] = underlying.get(key)
def iterator: Iterator[(K, V)] = underlying.iterator
def + [V1 >: V](kv: (K, V1)): MapWithRelationship[K, V1] = {
val (k, v) = kv
if (pred(k, v)) {
new MapWithRelationship[K, V1] {
val pred = self.pred
val underlying = self.underlying + kv
}
} else {
throw new Exception(s"Key-value pair $kv failed MapWithRelationship predicate")
}
}
def -(key: K): MapWithRelationship[K, V] =
new MapWithRelationship[K, V] {
val pred = self.pred
val underlying = self.underlying - key
}
}
object MapWithRelationship {
def apply[K, V](rule: (K, Any) => Boolean)(pairs: (K, V)*) = {
val empty = new MapWithRelationship[K, V] {
def pred = rule
def underlying = Map.empty[K, V]
}
pairs.foldLeft(empty)(_ + _)
}
}
Then you can use as such:
scala> val x = MapWithRelationship[Int, Int]((k, v) => v == k * k)()
x: MapWithRelationship[Int,Int] = Map()
scala> val x2 = x + (1 -> 1)
x2: MapWithRelationship[Int,Int] = Map(1 -> 1)
scala> val x3 = x + (5 -> 25)
x3: MapWithRelationship[Int,Int] = Map(5 -> 25)
scala> val x4 = x + (6 -> "foo")
java.lang.Exception: Key-value pair (6,foo) failed MapWithRelationship predicate
at MapWithRelationship$class.$plus(<console>:21)
at MapWithRelationship$$anon$3.$plus(<console>:33)
... 32 elided
You could make an infinite map of squares using:
val mySquareMap = Map.empty[Int, Int].withDefault(d => d * d)
This map will still have +, get, iterator and other methods that won't work as desired, but if you need a read-only map that returns squares, this would work.
Of course, it would be more efficient and probably clearer to just use:
val mySquare = (d:Int) => d * d
as a function. However, the above Map might be useful if you need to use some API that requires that type.
To have a more full-fledged solution for this, you might be better off building your own class that extends Map[Int, Int] that overrides get to return the square of its argument.
I have defined the object below. But I don't understand why the mapValues body only executes in test1. ie. why is the output:
Calling test1
Calling test2
Mapping: One
Mapping: Two
Mapped: Map(1 -> Xx, 2 -> Xx)
I have tested it with both scala 2.10 and 2.11 with the same results.
object Test {
def test1: Option[String] = {
val map = Map(1 -> "One", 2 -> "Two")
val mapped = map.mapValues { v =>
println("Mapping: " + v)
"Xx"
}
None
}
def test2: Option[String] = {
val map = Map(1 -> "One", 2 -> "Two")
val mapped = map.mapValues { v =>
println("Mapping: " + v)
"Xx"
}
println("Mapped: " + mapped)
None
}
def main(args: Array[String]): Unit = {
println("Calling test1")
test1
println("Calling test2")
test2
}
}
mapValues actually returns a view, so the results are computed lazily. From the scaladoc for mapValues:
return a map view which maps every key of this map to f(this(key)). The resulting map wraps the original map without copying any elements.
So for example:
val mapped = Map(1 -> "One", 2 -> "Two").mapValues { v =>
println("Mapping: " + v)
"Xx"
}
On it's own this will print nothing when declared. But as soon as mapped is accessed, the values will be computed, and the statements will be printed. (In fact, the values will be re-computed every time you access mapped)
In Test.test1, there is nothing accessing mapped, so the values are never computed.
In Test.test2, you're printing out mapped, which triggers the computation of the values.
The other answer explains the problem, but as a solution, if you want a strict map, just use normal map:
val m = Map(1 -> "One", 2 -> "Two")
val mapped = m.map {
case (k,v) => k -> {
println("Mapping: " + v)
"Xx"
}
}
Alternatively, you can define your own extension method to do what you want:
import scala.collection.GenTraversableLike
import scala.collection.generic.CanBuildFrom
implicit class HasMapVals[T, U, Repr](val self: GenTraversableLike[(T, U), Repr]) extends AnyVal {
def mapVals[R, That](f: U => R)(implicit bf: CanBuildFrom[Repr, (T, R), That]) = {
self.map { case (k,v) => k -> f(v) }
}
}
val m = Map(1 -> "One", 2 -> "Two")
val mapped = m.mapVals { v =>
println("Mapping: " + v)
"Xx"
}
I'm trying to use scala's LinkedHashMap as an LRU cache, but I'm not sure how to remove the oldest entry of such a map. I know java's LinkedHashMap has a method removeEldestEntry, but there does not seem to be a similar method for scala's implementation. I'd prefer not to convert to java's implementation just to have access to removeEldestEntry. How can I achieve this?
This will do what you want:
def removeOldestEntry[K](m: scala.collection.mutable.LinkedHashMap[K, _]): m.type =
m -= m.head._1
(Kudos to Jasper-M for pointing out that head will give the oldest entry)
You can do this in the following way:
object myApp {
def main(args: Array[String]) {
val myMap = new MyLinkedHashMap[Int,String]()
myMap.add(1, "a") // Map(1 -> a)
myMap.add(2, "b") // Map(1 -> a, 2 -> b)
myMap.add(3, "c") // Map(1 -> a, 2 -> b, 3 -> c)
myMap.add(4, "d") // Map(1 -> a, 2 -> b, 3 -> c, 4 -> d)
myMap.removeEldest // Map(2 -> b, 3 -> c, 4 -> d)
myMap.get(2) // Map(3 -> c, 4 -> d, 2 -> b)
myMap.removeEldest // Map(4 -> d, 2 -> b)
}
}
class MyLinkedHashMap[K,V] {
import scala.collection.mutable.LinkedHashMap
var map = new LinkedHashMap[K, V]()
/* adds an element to the HaskMap */
def add(key: K, value: V) {
map.put(key, value)
}
/* removes the LRU element from the HaskMap */
def removeEldest {
if (!map.isEmpty) {
map = map.drop(1)
}
}
/* gets the value for the given key and moves it to the top of the HashMap */
def get(key: K): Option[V] = {
val value = map.remove(key)
if (value != None) {
map.put(key, value.get)
}
return value
}
}
I am trying to define a "chained map" after http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html. I have run into a problem defining the companion object apply method. Here is what I have come up with:
import scala.collection.generic.ImmutableMapFactory
import scala.collection.immutable.HashMap
class ChainedMap[A, B](private val superMap: ChainedMap[A, B])
extends HashMap[A, B] {
override def get(key: A): Option[B] = {
if (contains(key)) {
get(key)
} else if (superMap != null) {
superMap.get(key)
} else {
None
}
}
}
object ChainedMap extends ImmutableMapFactory[ChainedMap] {
override def apply[A, B](superMap: ChainedMap[A, B],
elems: (A, B)*): ChainedMap[A, B] = {
// What goes here?
}
}
Here is how I will use it:
val parentMap = ChainedMap(null, "x" -> 1, "y" -> 2)
val childMap = ChainedMap(parentMap, "a" -> 42)
Well, extending Scala collections is tricky. There's this reference, plus some blogs and Stack Overflow questions. However, you don't need to do it, because it is already supported.
scala> val parentMap = Map("x" -> 1, "y" -> 2)
parentMap: scala.collection.immutable.Map[java.lang.String,Int] = Map((x,1), (y,2))
scala> val childMap = Map("a" -> 42) withDefault parentMap
childMap: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,42))
scala> childMap("x")
res5: Int = 1