scala mutable Map withDefaultValue strange behaviour - scala

I have this example which uses a mutable HashMap.withDefaultValue. withDefaultValues provides a way to return a value even if the key does not exist, but it should not modify the collection. in any case, there is a conflicting behaviour, as map.size returns 0, and at the same time map(key) returns a value.
how is this possible?
import scala.collection.mutable
val map = mutable.HashMap[String, mutable.Map[Int, String]]()
.withDefaultValue(mutable.HashMap[Int, String]())
map("id1")(2) = "three"
println(map.size) // 0 (expected)
println(map) // Map() (expected)
println(map("id1")) // Map(2 -> three) (unexpected)
println(map("id1")(2)) // three (unexpected)

It's possible to factor out defaultValue because it's passed as a value.
import scala.collection.mutable
val defaultValue = mutable.HashMap[Int, String]()
val map = mutable.HashMap[String, mutable.Map[Int, String]]()
.withDefaultValue(defaultValue)
map("id1")(2) = "three"
Which gives you
println(defaultValue) // Map(2 -> three)
... which should explain the rest of the behaviour. And that's exactly why I recommend immutable data structures ;-)

Related

How to create a Future of a map of a different type

I am using com.twitter.util.Future, scala 2.11.11
I have this piece of code that I'm trying to convert into a Future[Map[Long, String]]
val simpleMap: Map[Long, Int] = Map(1L -> 2, 2L -> 4)
val keyToNewFutureMap = Future.collect(simpleMap.map {
case (key, value) =>
val newFuture = getAFutureFromValue(value)
key -> newFuture
}.toSeq.toMap
)
val keyToFutureMap = Map(1L -> Future.value(1))
val futureMap = Future.collect(keyToFutureMap) // converts into a
Future[Map[Long, Int]]
Future.collect(Seq(futureMap, keyToNewFutureMap)) // Stuck here
I'm stuck here. I wanted to use the returned maps from both Futures and generate a new map. The new map will contain unique keys that appear in both futureMap and keyToNewFutureMap.
keyToFutureMap is given in the form of a Map[Long, Future[Option[Int]]], which is why I used a collect to turn it into a Future[Map[Long, Int]]
Any help is most appreciated.
If I understood correctly, you want this:
val newFutureMap = Future.traverse(simpleMap) {
case (key, value) =>
getAFutureFromValue(value).map(key -> _)
}.map(_.toMap)

Convert query string to map in scala

I have a query string in this form:
val query = "key1=val1&key2=val2&key3=val3
I want to create a map with the above key/value pairs. So far I'm doing it like this:
//creating an iterator with 2 values in each group. Each index consists of a key/value pair
val pairs = query.split("&|=").grouped(2)
//inserting the key/value pairs into a map
val map = pairs.map { case Array(k, v) => k -> v }.toMap
Are there any problems with doing it like I do? If so, is there some library I could use to do it?
Here is an approach using the URLEncodedUtils:
import java.net.URI
import org.apache.http.client.utils.URLEncodedUtils
import org.apache.http.{NameValuePair => ApacheNameValuePair}
import scala.collection.JavaConverters._
import scala.collection.immutable.Seq
object GetEncodingTest extends App {
val url = "?one=1&two=2&three=3&three=3a"
val params = URLEncodedUtils.parse(new URI(url), "UTF_8")
val convertedParams: Seq[ApacheNameValuePair] = collection.immutable.Seq(params.asScala: _*)
val scalaParams: Seq[(String, String)] = convertedParams.map(pair => pair.getName -> pair.getValue)
val paramsMap: Map[String, String] = scalaParams.toMap
paramsMap.foreach(println)
}
Assuming the query string you are working with is as simple as you showed, the use of grouped(2) is a great insight and gives a pretty elegant looking solution.
The next step from where you're at is to use the under-documented Array::toMap method:
val qs = "key=value&foo=bar"
qs.split("&|=") // Array(key, value, foo, bar)
.grouped(2) // <iterator>
.map(a => (a(0), a(1))) // <iterator>
.toMap // Map(key -> value, foo -> bar)
grouped(2) returns an Iterator[Array[String]], that's a little harder to follow because iterators don't serialize nicely on the Scala console.
Here's the same result, but a bit more step-by-step:
val qs = "key=value&foo=bar"
qs.split("&") // Array(key=value, foo=bar)
.map(kv => (kv.split("=")(0), kv.split("=")(1))) // Array((key,value), (foo,bar))
.toMap // Map(key -> value, foo -> bar)
If you want a more general solution for HTTP query strings, consider using a library for URL parsing.

Scala : How to find types of values inside a scala nested collection

consider the following variables in scala :
val nestedCollection_1 = Array(
"key_1" -> Map("key_11" -> "value_11"),
"key_2" -> Map("key_22" -> "value_22"))
val nestedCollection_2 = Map(
"key_3"-> ["key_33","value_33"],
"key_4"-> ["key_44"->"value_44"])
Following are my questions :
1) I want to read the values of the variables nestedCollection_1, nestedCollection_2 and ensure that the value of the variables are of the format
Array[Map[String, Map[String, String]]
and
Map[String, Array[String]]]
2) Is it possible to get the detailed type of a variable in scala? i.e. nestedColelction_1.SOME_METHOD should return Array[Map[String, Map[String, String]] as type of its values
I am not sure what exacltly do you mean. Compiler can ensure type of any variable if you just annotate the type:
val nestedCollection_2: Map[String, List[String]] = Map(
"key_3"-> List("key_33", "value_33"),
"key_4"-> List("key_44", "value_44"))
You can see type of variable in scala repl when you define it, or using Alt + = in Intellij Idea.
scala> val nestedCollection_2 = Map(
| "key_3"-> List("key_33", "value_33"),
| "key_4"-> List("key_44", "value_44"))
nestedCollection_2: scala.collection.immutable.Map[String,List[String]] = Map(key_3 -> List(key_33, value_33), key_4 -> List(key_44, value_44))
Edit
I think I get your question now. Here is how you can get type as String:
import scala.reflect.runtime.universe._
def typeAsString[A: TypeTag](elem: A) = {
typeOf[A].toString
}
Test:
scala> typeAsString(nestedCollection_2)
res0: String = Map[String,scala.List[String]]
scala> typeAsString(nestedCollection_1)
res1: String = scala.Array[(String, scala.collection.immutable.Map[String,String])]

Nested Map withDefaultValue changes default value

I have a mutable map containing another mutable map, both with default values. After I assign a value to one key in the enclosed map, its default value seems to change.
I.e. I expected anotherDefault to have the value Map(1 -> default), NOT Map(1 -> something).
Why is this happening?
scala> import scala.collection.mutable.{Map => MMap}
import scala.collection.mutable.{Map=>MMap}
scala> val amap = Map[Int, MMap[Int, String]]().withDefaultValue(MMap().withDefaultValue("default"))
amap: scala.collection.immutable.Map[Int,scala.collection.mutable.Map[Int,String]] = Map()
scala> val bmap = amap(2)
bmap: scala.collection.mutable.Map[Int,String] = Map()
scala> bmap(1)
res17: String = default
scala> bmap(1) = "something"
scala> val anotherDefault = amap(3)
anotherDefault: scala.collection.mutable.Map[Int,String] = Map(1 -> something)
The outer map (amap) is creating a single instance of the inner map to use as the default. When you access this via val bmap = amap(2), then modify bmap, you are modifying the single default map used by amap. When you call amap(3), you then get back this default map, which is now a map with the key/value pair (1 -> "something").
What you probably want is withDefault, not withDefaultValue, although it needs some extra argument/type specification to work:
val amap = Map[Int, MMap[Int, String]]().withDefault(x => MMap[Int, String]().withDefaultValue("default"))

How does Scala's mutable Map update [map(key) = newValue] syntax work?

I'm working through Cay Horstmann's Scala for the Impatient book where I came across this way of updating a mutable map.
scala> val scores = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
scores: scala.collection.mutable.Map[String,Int] = Map(Bob -> 3, Alice -> 10, Cindy -> 8)
scala> scores("Alice") // retrieve the value of type Int
res2: Int = 10
scala> scores("Alice") = 5 // Update the Alice value to 5
scala> scores("Alice")
res4: Int = 5
It looks like scores("Alice") hits apply in MapLike.scala. But this only returns the value, not something that can be updated.
Out of curiosity I tried the same syntax on an immutable map and was presented with the following error,
scala> val immutableScores = Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
immutableScores: scala.collection.immutable.Map[String,Int] = Map(Alice -> 10, Bob -> 3, Cindy -> 8)
scala> immutableScores("Alice") = 5
<console>:9: error: value update is not a member of scala.collection.immutable.Map[String,Int]
immutableScores("Alice") = 5
^
Based on that, I'm assuming that scores("Alice") = 5 is transformed into scores update ("Alice", 5) but I have no idea how it works, or how it is even possible.
How does it work?
This is an example of the apply, update syntax.
When you call map("Something") this calls map.apply("Something") which in turn calls get.
When you call map("Something") = "SomethingElse" this calls map.update("Something", "SomethingElse") which in turn calls put.
Take a look at this for a fuller explanation.
Can you try this: => to update list of Map
import java.util.concurrent.ConcurrentHashMap
import scala.collection.JavaConverters._
import scala.collection.concurrent
val map: concurrent.Map[String, List[String]] = new ConcurrentHashMap[String, List[String]].asScala
def updateMap(key: String, map: concurrent.Map[String, List[String]], value: String): Unit = {
map.get(key) match {
case Some(list: List[String]) => {
val new_list = value :: list
map.put(key, new_list)
}
case None => map += (key -> List(value))
}
}
The problem is you're trying to update immutable map. I had the same error message when my map was declared as
var m = new java.util.HashMap[String, Int]
But when i replaced the definition by
var m = new scala.collection.mutable.HashMap[String, Int]
the m.update worked.