scala.Some cannot be cast to scala.collection.immutable.Map Exception - scala

I'm new to scala,
val data = Map("test" -> data1,
"cat" -> None,
"myList" -> Map("test2" -> data2, "test3" -> data3))
val output = data.map(x =>
if (x._2.isInstanceOf[Map[String, Any]]) (x._1 -> Some(x._2))
else x)
Map(test -> data1,
cat -> None,
myList -> Some(Map(test2 -> data2, test3 -> data3)))
val valueToFieldMapping = output(fieldName).get.asInstanceOf[Map[String, String]]
I'm getting
java.lang.ClassCastException: scala.Some cannot be cast to scala.collection.immutable.Map
exception please help me if anyone has an idea about this. Thanks

Firstly, let's clean up the definition of output by using mapValues:
val output = data.mapValues(x =>
if (x.isInstanceOf[Map[String, Any]]) Some(x)
else x)
Then do this
val valueToFieldMapping = output(fieldName).asInstanceOf[Option[Map[String, String]]].get
You can't call get on the Some you generate when creating output because the compile doesn't know it is an Option yet.
Having said all that, the comments are right in saying that using Any and asInstanceOf is really ugly so you need to find a better way of expressing whatever it is you are trying to do. At the very least, use match rather than asInstanceOf so that you can implement the error case if the object is not what you think it is.

The problem is with this line you don't have .get property on instance of Object.
you don't need .get if you want to use .get method then do output.get(fieldName)
val valueToFieldMapping = output(fieldName).get.asInstanceOf[Map[String, String]]
output.get(fieldName) gives you the Option[Object] and you are trying to convert object into the instance of the Map[String, String]
there is no implicit conversion from Option to map so that's the reason you are getting the error:
java.lang.ClassCastException: scala.Some cannot be cast to scala.collection.immutable.Map
or you can do like this:
val valueToFieldMapping: Option[Map[String, String]] = output.get(fieldName).asInstanceOf[Option[Map[String, String]]]

Related

Scala Nested HashMaps, how to access Case Class value properties?

New to Scala, continue to struggle with Option related code. I have a HashMap built of Case Class instances that themselves contain hash maps with Case Class instance values. It is not clear to me how to access properties of the retrieved Class instances:
import collection.mutable.HashMap
case class InnerClass(name: String, age: Int)
case class OuterClass(name: String, nestedMap: HashMap[String, InnerClass])
// Load some data...hash maps are mutable
val innerMap = new HashMap[String, InnerClass]()
innerMap += ("aaa" -> InnerClass("xyz", 0))
val outerMap = new HashMap[String, OuterClass]()
outerMap += ("AAA" -> OuterClass("XYZ", innerMap))
// Try to retrieve data
val outerMapTest = outerMap.getOrElse("AAA", None)
val nestedMap = outerMapTest.nestedMap
This produces error: value nestedMap is not a member of Option[ScalaFiddle.OuterClass]
// Try to retrieve data a different way
val outerMapTest = outerMap.getOrElse("AAA", None)
val nestedMap = outerMapTest.nestedMap
This produces error: value nestedMap is not a member of Product with Serializable
Please advise on how I would go about getting access to outerMapTest.nestedMap. I'll eventually need to get values and properties out of the nestedMap HashMap as well.
Since you are using .getOrElse("someKey", None) which returns you a type Product (not the actual type as you expect to be OuterClass)
scala> val outerMapTest = outerMap.getOrElse("AAA", None)
outerMapTest: Product with Serializable = OuterClass(XYZ,Map(aaa -> InnerClass(xyz,0)))
so Product either needs to be pattern matched or casted to OuterClass
pattern match example
scala> outerMapTest match { case x : OuterClass => println(x.nestedMap); case _ => println("is not outerclass") }
Map(aaa -> InnerClass(xyz,0))
Casting example which is a terrible idea when outerMapTest is None, (pattern matching is favored over casting)
scala> outerMapTest.asInstanceOf[OuterClass].nestedMap
res30: scala.collection.mutable.HashMap[String,InnerClass] = Map(aaa -> InnerClass(xyz,0))
But better way of solving it would simply use .get which very smart and gives you Option[OuterClass],
scala> outerMap.get("AAA").map(outerClass => outerClass.nestedMap)
res27: Option[scala.collection.mutable.HashMap[String,InnerClass]] = Some(Map(aaa -> InnerClass(xyz,0)))
For key that does not exist, gives you None
scala> outerMap.get("I dont exist").map(outerClass => outerClass.nestedMap)
res28: Option[scala.collection.mutable.HashMap[String,InnerClass]] = None
Here are some steps you can take to get deep inside a nested structure like this.
outerMap.lift("AAA") // Option[OuterClass]
.map(_.nestedMap) // Option[HashMap[String,InnerClass]]
.flatMap(_.lift("aaa")) // Option[InnerClass]
.map(_.name) // Option[String]
.getOrElse("no name") // String
Notice that if either of the inner or outer maps doesn't have the specified key ("aaa" or "AAA" respectively) then the whole thing will safely result in the default string ("no name").
A HashMap will return None if a key is not found so it is unnecessary to do getOrElse to return None if the key is not found.
A simple solution to your problem would be to use get only as below
Change your first get as
val outerMapTest = outerMap.get("AAA").get
you can check the output as
println(outerMapTest.name)
println(outerMapTest.nestedMap)
And change the second get as
val nestedMap = outerMapTest.nestedMap.get("aaa").get
You can test the outputs as
println(nestedMap.name)
println(nestedMap.age)
Hope this is helpful
You want
val maybeInner = outerMap.get("AAA").flatMap(_.nestedMap.get("aaa"))
val maybeName = maybeInner.map(_.name)
Which if your feeling adventurous you can get with
val name: String = maybeName.get
But that will throw an error if its not there. If its a None
you can access the nestMap using below expression.
scala> outerMap.get("AAA").map(_.nestedMap).getOrElse(HashMap())
res5: scala.collection.mutable.HashMap[String,InnerClass] = Map(aaa -> InnerClass(xyz,0))
if "AAA" didnt exist in the outerMap Map object then the below expression would have returned an empty HashMap as indicated in the .getOrElse method argument (HashMap()).

Handle Any data type dynamically in Scala

I have a map in Scala returned by a function which is of type Map[String, Any]
For example:
val map: Map[String, Any] = Map("key1" -> "strVal", "key2" -> List[Map[String, Any]](), "key3" -> Map("k1" -> "v1"))
Now the problem is, to work on the value I get corresponding to a key, I've to use asInstanceOf[] every time. For eg,
val key2Hash = map.getOrElse("key3", Map()).getOrElse("k1", "")
throws error because the Map retrieved is of form Any and I have to use asInstanceOf[] for every situation as belows:
val key2Hash = map.getOrElse("key3", Map()).asInstanceOf[Map[String, String]].getOrElse("k1", "")
Is there a better way to do it? Or should I not be starting of with Map[String, Any] at the first place?
Map[String, Any]? You might as well use python directly!
Joking apart, you can get "nicer" casts syntax using pattern matching:
map.get("key3") match {
case Some(anotherMap: Map[String, String]) => ...
case _ => ...
}

Scala convert Seq[Object] to Map[String, Map[String, String]]

I am new to Scala so I am a bit fighting with maps.
I have
val items = Seq[MyModel]
where MyModel (came from Java) contains getLang, getName and getMessage methods.
Now I need to fill up the
var loadedMessagesMap: Map[String, Map[String, String]] = ListMap.empty
to contain values grouped by lang in structure: lang -> (name -> message). Name property is unique.
Thank you.
Maybe this will help you:
val result: Map[String, Map[String, Seq[String]]] = items.groupBy(_.getLang).map {
case(lang, models) =>
lang -> models.groupBy(_.getName).mapValues(_.map(_.getMessage))
}
It returns a Seq[String] because there might be several messages for the same language and name. Not sure how you want to handle that case.
This should do the trick:
val models: Seq[MyModel] = ???
val mapped = models.map { model =>
model.getLang -> Map(model.getName -> model.getMessage)
}.toMap
I hope this helps you.

Converting command line argument key=value pair to Map in scala

in my main program i receive inputs like -
key1=value1 key2=value2
Now what I want is to create a map out of it. I know the imperative way of doing this where I would get Array[String] that can be foreach and then split by "=" and then key and value can be used to form a Map.
is there a good functional and readable way to achieve this?
Also It will be great if I can avoid mutable Map and I want to avoid initial Dummy value initialization.
def initialize(strings: Array[String]): Unit = {
val m = collection.mutable.Map("dummy" -> "dummyval")
strings.foreach(
s => {
val keyVal:Array[String] = s.split("=")
m += keyVal(0) -> keyVal(1)
})
println(m)
}
you can just use toMap().
However, converting from array to tuple is not quite trivial:
How to convert an Array to a Tuple?
scala> val ar = Array("key1=value1","key2=value2")
ar: Array[String] = Array(key1=value1, key2=value2)
scala> ar.collect(_.split("=") match { case Array(x,y) => (x,y)}).toMap
res10: scala.collection.immutable.Map[String,String] = Map(key1 -> value1, key2 -> value2)
Maybe you have to call Function.unlift for intellij
val r = ar.collect(Function.unlift(_.split("=") match { case Array(x, y) => Some(x, y)})).toMap
similar to above but using only 'map'
ar.map(_.split("=")).map(a=>(a(0), a(1))).toMap
You can use Scopt to do the command line argument parsing in a neat way.

Is it possible to make json4s not to throw exception when required field is missing?

Is it possible to make json4s not to throw exception when required field is missing ?
When I "extract" object from raw json string it throws exception like this one
org.json4s.package$MappingException: No usable value for pager
No usable value for rpp
Did not find value which can be converted into byte
at org.json4s.reflect.package$.fail(package.scala:98)
at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$buildCtorArg(Extraction.scala:388)
at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$11.apply(Extraction.scala:396)
Is it possible just to let it be null ?
It's quite simple, you have to use Option and its potentials, Some and None.
val json = ("name" -> "joe") ~ ("age" -> Some(35));
val json = ("name" -> "joe") ~ ("age" -> (None: Option[Int]))
Beware though, in the above case a match will be performed for your Option. If it's None, it will be completely removed from the string, so it won't feed back null.
In the same pattern, to parse incomplete JSON, you use a case class with Option.
case class someModel(
age: Option[Int],
name: Option[String]
);
val json = ("name" -> "joe") ~ ("age" -> None);
parse(json).extract[someModel];
There is a method which won't throw any exception, and that is extractOpt
parse(json).extractOpt[someModel];
A way to replicate that with the scala API would be to use scala.util.Try:
Try { parse(json).extract[someModel] }.toOption
I've dealt with this problem when dealing with data migrations, and I wanted default values to fill undefined fields.
My solution was to merge the defaults into the JValue before extracting the result.
val defaultsJson = Extraction.decompose(defaults)
val valueJson = JsonUtil.jValue(v)
(defaultsJson merge valueJson).extract[T]
JsonUtil.scala
import org.json4s._
object JsonUtil {
import java.nio.charset.StandardCharsets.UTF_8
import java.io.{InputStreamReader, ByteArrayInputStream}
def jValue(json: String): JValue = {
jValue(json.getBytes(UTF_8))
}
def jValue(json: Array[Byte]): JValue = {
val reader = new InputStreamReader(new ByteArrayInputStream(json), UTF_8)
native.JsonParser.parse(reader)
}
}