8

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

Community
  • 1
  • 1
Jiří Vypědřík
  • 1,324
  • 12
  • 24
  • 1
    Are you sure you also want to match whether `myMap` is a Map? What else could it be? – ziggystar Sep 03 '13 at 08:05
  • What is your second case expecting to match against? What would myMap look like if it matched? – Shadowlands Sep 03 '13 at 08:28
  • 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 Sep 03 '13 at 10:02
  • 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 Sep 03 '13 at 10:09
  • 1
    It will help if you state the signature of the function you want to implement. – ziggystar Sep 03 '13 at 10:16
  • Cannot vote up @ziggystar's comment enough. – itsbruce Sep 03 '13 at 14:41
  • Simplifying, let it be `(Any)=>Option[String]` – Jiří Vypědřík Sep 03 '13 at 15:10

1 Answers1

1

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

Bask.ws
  • 823
  • 4
  • 6
  • PartialFunction's chained with andThen and option-ized via .lift is the way I have implemented this algorithm so far. However, I'm curious if it's possible to do in a more concise way. – Jiří Vypědřík Sep 03 '13 at 10:00
  • 1
    I think it is a mathematically wrong task. Using match to find element of unordered map can give unpredicted results – Bask.ws Sep 03 '13 at 10:33