Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using collect on maps in Scala

I recently stumbled over this post, which "introduces" the collect method for Scala collections. The usage is straight forward:

scala> val ints = List(1, "2", 3) collect { case i: Int => i }
ints: List[Int] = List(1, 3)

Now maps are basically lists of key-value pairs, which are represented by tuples in Scala. So you might wanna try something like this:

scala> val pairs = Map(1 -> "I", "II" -> 2)
pairs: scala.collection.immutable.Map[Any,Any] = Map(1 -> I, II -> 2)

scala> val intsToStrings = pairs collect { case pair: (Int, String) => pair }

The compiler complains of course due to the type erasure model of the JVM, so the first thing we try is using existential types:

scala> val intsToStrings = pairs collect { case pair: (_, _) => pair }
intsToString: scala.collection.immutable.Map[Any,Any] = Map(1 -> I, II -> 2)

Although the code passed the compiler, and the result is "correct" (we wanted pairs => we got pairs) we still didn't get what we actually wanted. The second attempt looks like this:

scala> val intsToStrings = pairs collect {
     |    case pair: (_, _) if pair._1.isInstanceOf[Int] && pair._2.isInstanceOf[String] => pair
     | }
intsToStrings: scala.collection.immutable.Map[Any,Any] = Map(1 -> I)

Ok, we are almost there:

scala> val realIntsToRealStrings = intsToStrings map {
     |    pair => (pair._1.asInstanceOf[Int], pair._2.asInstanceOf[String])
     | }
realIntsToRealStrings: scala.collection.immutable.Map[Int,String] = Map(1 -> I)

We did it, but instead of only casting from (Any,Any) to (Int,String) we actually copied each pair and thus created a new pair.

Now comes the question part. As I mentioned "The compiler complains of course..." I made it sound like I really know what I'm talking about. I don't! All I know is that Java didn't have generics from the beginning. At some point generics came into Java but not into the JVM. So the compiler checks all the types, but as soon as the code is running, JVM does not care for the parametric type. It only sees that it's a Map or a List but not that it's a Map[String, Int] or List[Int].

So here is my question.

With all the checking, casting and mapping, we managed to transfer a Map[Any,Any] to Map[String,Int]. Is there a better way of doing that? I mean the types are there, JVM just does not see them (as far as I am concerned)...

like image 283
agilesteel Avatar asked Jul 10 '11 11:07

agilesteel


People also ask

How do you access map elements in Scala?

Scala Map get() method with example The get() method is utilized to give the value associated with the keys of the map. The values are returned here as an Option i.e, either in form of Some or None. Return Type: It returns the keys corresponding to the values given in the method as argument.

What is use of collect in Scala?

The collect function is applicable to both Scala's Mutable and Immutable collection data structures. The collect method takes a Partial Function as its parameter and applies it to all the elements in the collection to create a new collection which satisfies the Partial Function.

How do you add a value to a map in Scala?

We can insert new key-value pairs in a mutable map using += operator followed by new pairs to be added or updated.


1 Answers

pairs collect { case p @ (_: Int, _: String) => p.asInstanceOf[(Int, String)] }

or more concise, but with some overhead, I think

pairs collect { case (x: Int, y: String) => (x, y) }
like image 195
incrop Avatar answered Nov 15 '22 19:11

incrop