0

I have a certain map built from JSON and it has some hierarchy. For example:

"2015-05": {
    "129557": {          
        "123456": 3,
        "654321": 2,
        "143526": 1
     }
}

This is stored as a nested map. I wanted to have a simple method to access a key. I could do multiple checks at each key and then see if its present or not and then do it for the second key and so on. However this seems cumbersome. I instead chose to do something like this:

def getNumFromMap(key1: String, key2: Int, key3: String): Option[Int] ={
  try{
    map(key1)(key2).get(key3)
  }catch{
    case e: Exception => None
  }
}

This function could be potentially executed millions of times. Does using a try/catch slow down the execution? Is there a better method to accomplish the same which is faster?

Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
Sohaib
  • 4,556
  • 8
  • 40
  • 68

4 Answers4

8

Using a for comprehension :

type MMMap[K1, K2, K3, V] = Map[K1, Map[K2, Map[K3, V]]]

def getFromMMMap[K1, K2, K3, V](
  mmmap: MMMap[K1, K2, K3, V], k1: K1, k2: K2, k3: K3
) : Option[V] =
  for {
    mmap <- mmmap.get(k1)
    map <- mmap.get(k2)
    v <- map.get(k3)
  } yield v

For an MMMap[String, Int, String, Int] :

val mmmap = Map ("a" -> Map(1 -> Map("b" -> 2)))
scala> getFromMMMap(mmmap, "a", 1, "b")
res7: Option[Int] = Some(2)
Peter Neyens
  • 9,770
  • 27
  • 33
1

What about using a for expression?

def getNumFromMap(key1: String, key2: Int, key3: String): Option[Int] ={
    for {
        m1 <- map.get(1)
        m2 <- m1.get(2)
        m3 <- m2.get(3)
    } yield m3
}

However, it's hard to say which approach is faster without measuring it properly.

marstran
  • 26,413
  • 5
  • 61
  • 67
1
  def getNumFromMap(m: Map[String, Map[Int, Map[String, Int]]], key1: String, key2: Int, key3: String): Option[Int] = {
    m.get(key1).flatMap(_.get(key2)).flatMap(_.get(key3))
  }
ka4eli
  • 5,294
  • 3
  • 23
  • 43
0

Or, the one liner version:

def get3[A, B, C, V](m: Map[A,Map[B,Map[C,V]]], a:A, b:B, c:C) : Option[V] =
  m.get(a).flatMap(_.get(b).flatMap(_.get(c)))

Or just for fun, the Pimp version

implicit final class Map3[A,B,C,V](val self : Map[A,Map[B,Map[C,V]]]) extends AnyVal {
  def get3(a:A, b:B, c:C) : Option[V] =
    self.get(a).flatMap(_.get(b).flatMap(_.get(c)))
}

And then:

val m : Map[Int, Map[String, Map[Boolean, Char]]] = Map(5 -> Map("a" -> Map(true -> 'c')))
m.get3(5, "a", true)