8
votes

Is there any Scala trick to enable pattern matching against map keys? In other words, I'd like to have an extractor that beside the Map instance accepted also a key value that would mean I want this pattern to match only if the matchable value is an instance of Map and there is an entry with the given key in it and the value for this entry be subject to recursive pattern matching.

Something like this:

myMap match {
    case MyMap("a")(a) => // do smth with value a
    case MyMap("b")(MyMap("c")(c)) => // do smth with value c
}

Update:

I've found some way to approach closer to the goal, but it's still not perfect because it implies definition of synthetic key-value-holders:

case class MapKey[K](key: K) {
  def unapply(o: Any) = o match {
    case m: Map[K, _] ⇒ m.get(key)
    case _ ⇒ None
  }
}

val m1 = Map("a" → "aa", "b" → Map("c" → "cc"))
val m2 = Map("a" → "aa", "d" → "dd")

val b = MapKey("b")
val c = MapKey("c")
val d = MapKey("d")

for (m ← List(m1, m2)) m match {
  case b(c(x)) ⇒ println(s"b > c: $x")
  case d(x) ⇒ println(s"d: $x")
}

Similar question: Can extractors be customized with parameters in the body of a case statement (or anywhere else that an extractor would be used)?

Feature request: SI-5435

1
Are you sure you also want to match whether myMap is a Map? What else could it be? - ziggystar
What is your second case expecting to match against? What would myMap look like if it matched? - Shadowlands
This is a simplified example. Actually, I'm developing a Scala library for operations on Apple Cocoa types derived from NSObject. Particularly, the task is to parse the iOS Plist file which is very sophisticated with multiple alternative ways to describe the same thing. - Jiří Vypědřík
My initial solution was based on map-like operations returning Option by key, but the source code was polluted with lots of chained case _ => None, so I decided to develop a helper that chains provided PartialFunction's with andThen and lift, but the algorithm still looks ugly. Then I asked myself if it's possible to do the same with pattern matching. - Jiří Vypědřík
It will help if you state the signature of the function you want to implement. - ziggystar

1 Answers

1
votes

Maybe you are looking for solution that you don't really need? Can't imagine extractors here. You can use PF if you want to match key-value pairs:

val map = Map[String, String]("a" -> "b")

def matchTuple[A,B,C](map: Map[A,B])(pf: PartialFunction[(A,B), C]) = 
  map.collectFirst(pf)

matchTuple(map) {
  case ("a", b) => println("value for a is " + b)
}

Return type is Option[Unit] because we use collectFirst and println