7

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.

2
  • 5
    If you know they are strings, why does the compiler think they are Any? Commented Oct 9, 2014 at 4: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. Commented Oct 9, 2014 at 16:15

4 Answers 4

7

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)
Sign up to request clarification or add additional context in comments.

Comments

3

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 Anys 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)

2 Comments

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)
@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.
2

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 Strings (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)

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.