I'm using ListMap because I need to keep the insertion order in place. After initializing it seems it works. but when I call updated on it the order gets messed up. 1- Why is that? 2- Is there any other MapLike that doesn't have this problem, if not how should I update the map without problem?
scala> import scala.collection.immutable.ListMap
import scala.collection.immutable.ListMap
scala> val a = ListMap(0 -> "A", 1 -> "B", 2 ->"C")
a: scala.collection.immutable.ListMap[Int,String] = Map(0 -> A, 1 -> B, 2 -> C)
scala> a.foreach(println)
(0,A)
(1,B)
(2,C)
scala> val b = a.updated(1, "D")
b: scala.collection.immutable.ListMap[Int,String] = Map(0 -> A, 2 -> C, 1 -> D)
scala> b.foreach(println)
(0,A)
(2,C)
(1,D)
I could not find any existent immutable collection with desired property. But it could be crafted manually.
import scala.collection.immutable.{IntMap, Map, MapLike}
class OrderedMap[K, +V] private[OrderedMap](backing: Map[K, V], val order: IntMap[K], coorder: Map[K, Int], extSize: Int)
extends Map[K, V] with MapLike[K, V, OrderedMap[K, V]] {
def +[B1 >: V](kv: (K, B1)): OrderedMap[K, B1] = {
val (k, v) = kv
if (backing contains k)
new OrderedMap(backing + kv, order, coorder, extSize)
else new OrderedMap(backing + kv, order + (extSize -> k), coorder + (k -> extSize), extSize + 1)
}
def get(key: K): Option[V] = backing.get(key)
def iterator: Iterator[(K, V)] = for (key <- order.valuesIterator) yield (key, backing(key))
def -(key: K): OrderedMap[K, V] = if (backing contains key) {
val index = coorder(key)
new OrderedMap(backing - key, order - index, coorder - key, extSize)
} else this
override def empty: OrderedMap[K, V] = OrderedMap.empty[K, V]
}
object OrderedMap {
def empty[K, V] = new OrderedMap[K, V](Map.empty, IntMap.empty, Map.empty, 0)
def apply[K, V](assocs: (K, V)*): OrderedMap[K, V] = assocs.foldLeft(empty[K, V])(_ + _)
}
Here order is preserved insertion order map (probably with "holes"). coorder special field needed for efficient handling element removal. extSize is basically order.lastkey + 1 but more straightforward
Now you can verify that
val a = OrderedMap(0 -> "A", 1 -> "B", 2 -> "C")
a.foreach(println)
val b = a.updated(1, "D")
b.foreach(println)
prints
(0,A)
(1,B)
(2,C)
and
(0,A)
(1,D)
(2,C)
From the scala doc for updated
"This method allows one to create a new map with an additional mapping
from key to value."
Note it does not say "with a different value of an existing key". So when you updated with 1->D, that's a new/additional mapping. So it appears at the end of the list, preserving insertion order. The old mapping 1->C is no longer present in the map.
So it's not "messed up" and it's not a problem. It's doing what it's documented to do, the mappings are in insertion order.
Related
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 am learning the book functional programming in scala. chapter 10, exercise 20, to implement the following method:
def frequencyMap(strings: IndexedSeq[String]): Map[String, Int]
honestly, I don't have a solution, so, I checked the answer from GIT:
def mapMergeMonoid[K, V](V: Monoid[V]): Monoid[Map[K, V]] =
new Monoid[Map[K, V]] {
def zero = Map()
def op(a: Map[K, V], b: Map[K, V]) =
a.map {
case (k, v) => (k, V.op(v, b.get(k) getOrElse V.zero))
}
}
def bag[A](as: IndexedSeq[A]): Map[A, Int] =
foldMapV(as, mapMergeMonoid[A, Int](intAddition))((a: A) => Map(a -> 1))
def frequencyMap(strings: IndexedSeq[String]): Map[String, Int] = bag(strings)
But, when I tried to run a test, I got wrong answer:
frequencyMap(Vector("a rose", "is a", "rose is", "a rose"))
the answer printed:
Map(a rose -> 1)
the expected result:
Map(a -> 3, rose -> 3, is -> 2)
I can't figure out where is wrong with the implementation. could someone explain it for me? thanks.
Edit after the correct answer, and update with a correct implementation
def mapMergeMonoid[K, V](V: Monoid[V]): Monoid[Map[K, V]] =
new Monoid[Map[K, V]] {
def zero = Map()
def op(a: Map[K, V], b: Map[K, V]) = {
// (a.map {
// case (k, v) => (k, V.op(v, b.get(k) getOrElse V.zero))
//
val c = for {
(ka, va) <- a
(kb, vb) <- b
if (ka == kb)
} yield (ka -> V.op(va, vb))
a ++ b ++ c
}
}
def bag[A](as: IndexedSeq[A]): Map[A, Int] =
foldMapV(as, mapMergeMonoid[A, Int](intAddition))((a: A) => Map(a -> 1))
def frequencyMap(strings: IndexedSeq[String]): Map[String, Int] = bag(strings.map(_.split("\\s+")).flatten)
I don't have the book, but following the code from here, I can point out a few things for you to think about( based on some println()s in a REPL session ):
As S.R.I pointed out, you have a list of phrases, not words. The functions on github don't do anything to separate the word groups for you, so at best, your current input Vector could get:
Map(a rose -> 2, is a -> 1, rose is -> 1)
You could create a list of words by doing the following to your Vector:
Vector("a rose", "is a", "rose is", "a rose") map( _.split( " " ).toSeq ) flatten
The mapMergeMonoid function only appears to add the value of k and v together when there keys(k) are the same. Meaning an unsorted list is going to result in a lot of Map(string -> 1).
You could sort the Vector of words by making the following changes to Vector:
(Vector("a rose", "is a", "rose is", "a rose") map( _.split( " " ).toSeq ) flatten) sortWith(_.compareTo(_) < 0)
While foldMapV does split out all the phrases or words from the Vector in a Map[String, Int], it only appears to return the leftmost desired merge for a sorted IndexedSeq. With a sorted Vector of words as I've indicated, I got the result Map(a -> 3) from your implementation of frequencyMap. I would be inclined to use something other than foldMapV though or modify it to make frequencyMap work. The bit that appears to be missing to me is an accumulation of the results of the function it's applying into one large Map. I would also try a more "standard" head/tail recursion than the splitAt() calls(as I'm not convinced the foldMapV was handling is and rose very well).
I have a list which I am combining to a map in this way, by calling the respective value calculation function. I am using collection.breakout to avoid creating unnecessary intermediate collections since what I am doing is a bit combinatorial, and every little bit of saved iterations helps.
I need to filter out certain tuples from the map, in my case where the value is less than 0. Is it possible to add this to the map itself rather than doing a filter afterwards (thus iterating once again)?
val myMap: Map[Key, Int] = keyList.map(key => key -> computeValue(key))(collection.breakOut)
val myFilteredMap = myMap.filter(_._2 >= 0)
In other words I wish to obtain the second map ideally at one go, so ideally in the first call to map() I filter out the tuples I don't want. Is this possible in any way?
You can easily do this with a foldLeft:
keyList.foldLeft( Map[Key,Int]() ) {
(map, key) =>
val value = computeValue(key)
if ( value >= 0 ) {
map + (key -> value)
} else {
map
}
}
It would probably be best to do a flatMap:
import collection.breakOut
type Key = Int
val keyList = List(-1,0,1,2,3)
def computeValue(i: Int) = i*2
val myMap: Map[Key, Int] =
keyList.flatMap { key =>
val v = computeValue(key)
if (v >= 0) Some(key -> v)
else None
}(breakOut)
You can use collect
val myMap: Map[Key, Int] =
keyList.collect {
case key if computeValue(key) >= 0 => key -> computeValue(key)
}(breakOut)
But that requires re-computing computeValue(key), which is silly. Collect is better when you filter then map.
Or make your own method!:
import scala.collection.generic.CanBuildFrom
import scala.collection.TraversableLike
implicit class EnrichedWithMapfilter[A, Repr](val self: TraversableLike[A, Repr]) extends AnyVal {
def maptofilter[B, That](f: A => B)(p: B => Boolean)(implicit bf: CanBuildFrom[Repr, (A, B), That]): That = {
val b = bf(self.asInstanceOf[Repr])
b.sizeHint(self)
for (x <- self) {
val v = f(x)
if (p(v))
b += x -> f(x)
}
b.result
}
}
val myMap: Map[Key, Int] = keyList.maptofilter(computeValue)(_ >= 0)(breakOut)
When I create an immutable map with a standard call to Map() or by concatenating the existing maps created that way, in all my tests I get that traversing its members provides them in the order of addition. That's exactly the way I need them to be sorted, but there's not a word in the documentation about the reliability of the ordering of the members of the map.
So I was wondering whether it is safe to expect the standard Map to return its items in the order of addition or I should look for some other implementations and which ones in that case.
I don't think it's safe, the order is not preserved starting from 5 elements (Scala 2.9.1):
scala> Map(1 -> 1, 2 -> 2, 3 -> 3, 4 -> 4, 5 -> 5)
res9: scala.collection.immutable.Map[Int,Int] =
Map(5 -> 5, 1 -> 1, 2 -> 2, 3 -> 3, 4 -> 4)
With bigger maps the order is completely "random", try Map((1 to 100) zip (1 to 100): _*).
Try LinkedHashMap for ordered entries and TreeMap to achieve sorted entries.
There is no promise about the order of Map. There is an OrderedMap in scalas collection package. The values in that package are ordered by an implicit Ordering. As quickfix I recommend you to use a list of keys for the ordering of your Map.
var keyOrdering = List[Int]()
var unorderedMap = Map[Int, String]()
unorderedMap += (1 -> "one")
keyOrdering :+= 1
Edit
You could implement your own Ordering and pass it to a SortedMap as well.
Edit #2
A simple example would be the following:
scala> import scala.collection.SortedMap
import scala.collection.SortedMap
scala> implicit object IntOrdering extends Ordering[Int]
| def compare(a: Int, b: Int) = b - a
| }
defined module IntOrdering
scala> var sm = SortedMap[Int, String]()
sm: scala.collection.SortedMap[Int,String] = Map()
scala> sm += (1 -> "one")
scala> sm += (2 -> "two")
scala> println(sm)
Map(2 -> two, 1 -> one)
The implicit Ordering is applied to the keys, so IntOrdering might be applied to a SortedMap[Int, Any].
Edit #3
A self ordering DataType like in my comment might look this way:
case class DataType[T](t: T, index: Int)
object DataType{
private var index = -1
def apply[T](t: T) = { index += 1 ; new DataType[T](t, index)
}
Now we need to change the Ordering:
implicit object DataTypeOrdering extends Ordering[DataType[_]] {
def compare(a: DataType[_], b: DataType[_]) = a.index - b.index
}
I hope this is the way you expected my answer.
After digging I've found out that there exists an immutable ListMap that behaves exactly as I want it, but according to this table its performance is just awfull. So I wrote a custom immutable implementation that should perform effectively on all operations except removal, where it performs linearly. It does require a bit more memory as it's backed by a standard Map and a Queue, which itself utilizes a List twice, but in the current age it's not an issue, right.
import collection.immutable.Queue
object OrderedMap {
def apply[A, B](elems: (A, B)*) =
new OrderedMap(Map(elems: _*), Queue(elems: _*))
}
class OrderedMap[A, B](
map: Map[A, B] = Map[A, B](),
protected val queue: Queue[(A, B)] = Queue()
) extends Map[A, B] {
def get(key: A) =
map.get(key)
def iterator =
queue.iterator
def +[B1 >: B](kv: (A, B1)) =
new OrderedMap(
map + kv,
queue enqueue kv
)
def -(key: A) =
new OrderedMap(
map - key,
queue filter (_._1 != key)
)
override def hashCode() =
queue.hashCode
override def equals(that: Any) =
that match {
case that: OrderedMap[A, B] =>
queue.equals(that.queue)
case _ =>
super.equals(that)
}
}
I have a list of parent keys, each of which could possibly have zero or more associated values. I am not sure which collection to use.
I am using Map[Int,List[String]]
I am declaring the Map as
var nodes = new HashMap[Int, List[String]]
Then I have two methods to handle adding new elements. The first is to add new keys addNode and the second is to add new values addValue. Initially, the key will not have any values associated with it. Later on, during execution, new values will be associated.
def addNode(key: Int) = nodes += (key -> "")
def addValue(key: Int, value: String) = ???
I am not sure how to implement addValues
Update:
In response to #oxbow-lakes answer, This is the error I am receiving. Please note that keys need not have values associated with them.
scala> var nodes = Map.empty[Int, List[String]]
nodes: scala.collection.immutable.Map[Int,List[String]] = Map()
scala> nodes += (1->null)
scala> nodes += (1 -> ("one" :: (nodes get 1 getOrElse Nil)))
java.lang.NullPointerException
at .<init>(<console>:9)
at .<clinit>(<console>)
at .<init>(<console>:11)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
at java.lang.Thread.run(Thread.java:680)
Update 2:
The problem with the code above is the line nodes += (1->null) the key should be associated with Nil instead. Below is the working code.
scala> var nodes = Map.empty[Int, List[String]]
nodes: scala.collection.immutable.Map[Int,List[String]] = Map()
scala> nodes += (1->Nil)
scala> nodes += (1 -> ("one" :: (nodes get 1 getOrElse Nil)))
scala> nodes
res27: scala.collection.immutable.Map[Int,List[String]] = Map(1 -> List(one))
Using MultiMap
You possibly want to use MultiMap, which is a mutable collection isomorphic to Map[K, Set[V]]. Use as follows:
import collection.mutable
val mm = new mutable.HashMap[Int, mutable.Set[String]] with mutable.MultiMap[Int, String]
Then you add your nodes:
mm addBinding (key, value)
Without MultiMap
The alternative is to stick with immutable values. Assuming you want to avoid using lenses (see scalaz), you can add nodes as follows:
nodes += (key -> (value :: (nodes get key getOrElse Nil)))
Here it is working (in response to your comment):
scala> var nodes = Map.empty[Int, List[String]]
nodes: scala.collection.immutable.Map[Int,List[String]] = Map()
scala> def addNode(key: Int, value: String) =
| nodes += (key -> (value :: (nodes get key getOrElse Nil)))
addNode: (key: Int, value: String)Unit
scala> addNode(1, "Hi")
scala> addNode(1, "Bye")
scala> nodes
res2: scala.collection.immutable.Map[Int,List[String]] = Map(1 -> List(Bye, Hi))
Using Scalaz
Using the scalaz library, you can realize that this is simply using the Empty pattern:
nodes += (key -> (value :: ~(nodes get key)))
Or you could take advantage of the fact that Map is a monoid:
nodes = nodes |+| Map(key -> List(value))
In addition to #oxbow_lakes' answer, here's a idea for how you could use an addMap method that correctly adds two maps together (ie, combining lists for matching keys, adding new lists for new keys):
class EnhancedListMap(self: Map[Int,List[String]]) {
def addMap(other: Map[Int,List[String]]) =
(this.ungroup ++ enhanceListMap(other).ungroup)
.groupBy(_._1)
.mapValues(_.map(_._2))
def ungroup() =
self.toList.flatMap{ case (k,vs) => vs.map(k -> _) }
}
implicit def enhanceListMap(self: Map[Int,List[String]]) = new EnhancedListMap(self)
And you'd use it like this:
val a = Map(1 -> List("a","b"), 2 -> List("c","d"))
val b = Map(2 -> List("e","f"), 3 -> List("g","h"))
a addMap b
//Map(3 -> List(g, h), 1 -> List(a, b), 2 -> List(c, d, e, f))
You can include addNode, addValue, and addValues the same way (to EnhancedListMap above):
def addNode(key: Int) =
if(self contains key) self else self + (key -> Nil)
def addValue(key: Int, value: String) =
self + (key -> (value :: (self get key getOrElse Nil)))
def addValues(key: Int, values: List[String]) =
self + (key -> (values ::: (self get key getOrElse Nil)))
And then use them together:
var nodes = Map.empty[Int, List[String]]
// Map()
nodes = nodes.addNode(1)
// Map(1 -> List())
nodes = nodes.addValue(1,"a")
// Map(1 -> List(a))
nodes = nodes.addValue(2,"b")
// Map(1 -> List(a), 2 -> List(b))
nodes = nodes.addValues(2,List("c","d"))
// Map(1 -> List(a), 2 -> List(c, d, b))
nodes = nodes.addValues(3,List("e","f"))
// Map(1 -> List(a), 2 -> List(c, d, b), 3 -> List(e, f))
nodes = nodes.addMap(Map(3 -> List("g","h"), 4-> List("i","j")))
// Map(1 -> List(a), 2 -> List(c, d, b), 3 -> List(e, f, g, h), 4 -> List(i, j))
I quite like the getOrElseUpdate method provided by mutable maps:
import scala.collection.mutable._
private val nodes = new HashMap[Int, Buffer[String]]
def addNode(key: Int): Unit =
nodes.getOrElseUpdate(key, new ArrayBuffer)
def addValue(key: Int, value: String): Unit =
nodes.getOrElseUpdate(key, new ArrayBuffer) += value