New to scala - How to create a Map[String,String]
from Map[String, Any]
The values of the Map[String,Any]
are strings but I don't know how to cast or otherwise coerce the "Any
" type to a "String
" type.
-
5If you know they are strings, why does the compiler think they are Any? – stew Oct 09 '14 at 04:11
-
I have a function that retrieves the param names and values from case classes. It returns them as Map[String,Any] because the values could be of any type. I'm used to Java where I can just cast to the type I want. I discovered that I can use 'Any.asInstanceOf[String]' to get the same effect. – coolgar Oct 09 '14 at 16:15
4 Answers
As you mentioned that all the values in your map are strings, you can simply use asInstanceOf
. If your assumption is incorrect, you will receive runtime exceptions as demonstrated below:
$ scala
Welcome to Scala version 2.9.2 (OpenJDK 64-Bit Server VM, Java 1.7.0_55).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val m:Map[String, Any] = Map("foo" -> 5, "bar" -> 7.6, "baz" -> "qux")
m: Map[String,Any] = Map(foo -> 5, bar -> 7.6, baz -> qux)
scala> val m2: Map[String, Any] = Map("foo" -> "5", "bar" -> "7.6", "baz" -> "qux")
m2: Map[String,Any] = Map(foo -> 5, bar -> 7.6, baz -> qux)
scala> m2.asInstanceOf[Map[String, String]]
res0: Map[String,String] = Map(foo -> 5, bar -> 7.6, baz -> qux)
This is perfect when all values are actually of type String
.
scala> res0("foo")
res5: String = 5
Watch out for your wrong assumption:
scala> m.asInstanceOf[Map[String, String]]
res2: Map[String,String] = Map(foo -> 5, bar -> 7.6, baz -> qux)
scala> res2("foo")
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at .<init>(<console>:10)
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:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
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:744)

- 8,257
- 4
- 43
- 61
Having a Map[String, Any]
in the first place is a sign something has probably gone off the rails, although without any code to look at, I can't really help you there. Typically, when you start to see Any
s and other super-generic types inferred, it's Scala's type system telling you that the code you've written does not mean what you think it means.
If you really want to do this, you can do:
scala> val m = Map("foo" -> 5, "bar" -> 7.6, "baz" -> "qux")
m: scala.collection.immutable.Map[String,Any] = Map(foo -> 5, bar -> 7.6, baz -> qux)
scala> m.mapValues(_.toString)
res0: scala.collection.immutable.Map[String,String] = Map(foo -> 5, bar -> 7.6, baz -> qux)

- 34,571
- 6
- 57
- 100
-
it doesn't necessarily mean code has gone off the rails, it can just mean that someone had to use a java library in their scala code (like a ServletRequest parameter map) – Andrew Norman Feb 10 '16 at 22:54
-
@AndrewNorman Hence "probably" :) I agree there are legitimate reasons to have an `Any`, but I stick with the assessment that it's probably indicative of a logical error, and the least-upper bound being inferred of some set of unrelated types. – acjay Feb 22 '16 at 21:22
As the others have already mentioned, the fact that have an Any
in the first place, is a sign that something somewhere has gone wrong. The correct solution would be to figure out what went wrong where and fix it.
However, if you really want to cast all the values to String
s (which is circumventing the type system, and thus something you shouldn't do), here's how you do it:
val anyMap: Map[String, Any] = Map("foo" -> "bar", "baz" -> "qux")
val stringMap = anyMap.mapValues(_.asInstanceOf[String])
// => stringMap: Map[String, String] = Map(foo -> bar, baz -> qux)

- 363,080
- 75
- 446
- 653
An example of Map[String,Any]
as inferred by the compiler includes
val a = Map( "s" -> 1, "t" -> "w" )
a: scala.collection.immutable.Map[String,Any] = Map(s -> 1, t -> w)
where the least common type for Int
and String
is Any
.
Apply toString
to each value in the map.

- 20,117
- 14
- 67
- 113