6
votes

Suppose I have a map m: Map[Any, Int]. Now I would like to take only entries (String, Int) from m and create a new map m1: Map[String, Int] with those entries.

I am trying to do the following:

val m1: Map[String, Int] = m collect {case e:(String, Int) => e}

It seems working but I get a warning: non variable type-argument String in type pattern (String, Int) is unchecked since it is eliminated by erasure.

How can I get rid of the warning?

3

3 Answers

9
votes

you probably want:

val m1: Map[String, Int] = m collect {case (k:String, v:Int) => k->v}
3
votes

(Just for reference. What you want is virtualeyes’s answer.)

val m1: Map[String, Int] = m flatMap { e =>
  e._1 match {
    case e1: String => Some(e1 -> e._2)
    case _ => None
  }
}
2
votes

Careful testing will show that your solution actually matches everything in the map, not just entries of type (String,Int). The warning from the compiler is telling you that the types of your match will be thrown away at runtime so your code is actually doing something like this:

val m1: Map[String, Int] = m collect {case e:Tuple2[Any,Any] => e.asInstanceOf[Tuple2[String,Int]]}

And the asInstanceOf call will not blow up since it only casts to a Tuple2 and the (String,Int) bit gets lost again because of erasure. You'll get a nasty failure when you try to iterate against the result though ...

Try this in the REPL

  val m:Map[String,Int] = Map("2" -> 3, 3 -> 4, "6" -> 10) collect {case e:(String, Int) => e}
  // Oops, thought we would get '2'
  m size 
  // Nothing wrong with this code except m contains some
  // hidden nasties which causes us to blow up
  for ((s:String, i:Int) <- m) yield s.length + i 

`