mkolarek
mkolarek

Reputation: 527

Recurisvely iterate through nested Map[String, Any]

I have an arbitrarily nested Map[String, Any] that I want to traverse and update values given a certain path.

For example, if the nested Map is

Map("a" -> "1", "b" -> Map("c" -> "2"))

and the path is ["b", "c"] I expect the result to be

Map("a" -> "1", "b" -> Map("c" -> "null"))

What I've tried so far is

def updateMapValue(
                map: Map[String, Any],
                path: List[String]
              ): Map[String, Any] =
map map {
  case (k: String, v: String) =>
    if (k.contains(path.head)) k -> "null"
    else k -> v
  case (k: String, v: Map[String @unchecked, _]) => updateMapValue(v, path.tail)
}

but the compilation error I get here is

Expression of type Iterable[Equals] doesn't conform to expected type Map[String, Any]

which happens when I try to call updateMapValue recursively.

What is happening here and is there an easier way to traverse nested Maps?

Upvotes: 2

Views: 504

Answers (1)

Krzysztof Atłasik
Krzysztof Atłasik

Reputation: 22635

You've got several problems with your code. First of all, you're using head which would throw an exception in the case, there is no head on path list (and it would happen if you passed the wrong path to the method).

Secondly, here case (k: String, v: Map[String, _]) => updateMapValue(v, path.tail) you're returning map, with wrong type, you probably meant k -> updateMapValue(v, path.tail).

Here's my implementation:

def updateMapValue(
                    map: Map[String, _],
                    path: List[String]
                  ): Map[String, _] = {

  path match { //we iterated over path getting head
    case x :: xs => map.map {
      case `x` -> (m: Map[String, _]) => x -> updateMapValue(m, xs) //if value is map go deeped
      case `x` -> (_: String) => x -> "null" //if value is String replace with "null"
      case w => w
    }
    case Nil => map
  }

}


val m = Map("a" -> "1", "b" -> Map("c" -> "2"))
val path = List("b", "c")
updateMapValue(m, path) //Map(a -> 1, b -> Map(c -> null))

This method is not stack-safe, because it's not tail-recursive, but if you know your maps won't be very deep it should do the job. You can make it stack-safe with TailCalls though.

Upvotes: 2

Related Questions