1

I have nested maps with a key -> Map(key1 -> Map(), key2 -> Map()) kinda representation, which basically represent the path structure of a particular HTTP request made.

root/twiki/bin/edit/Main/Double_bounce_sender root/twiki/bin/rdiff/TWiki/NewUserTemplate

I have stored them in a Map of maps which would give me the hierarchy of the path. Using a parser I read the data off server logs and get the corresponding data needed and then index the data in a sorted map.

val mainList: RDD[List[String]] = requesturl flatMap ( r => r.toString split("\\?") map (x => parser(x.split("/").filter(x => !x.contains("=")).toList).valuesIterator.toList))

def parser(list: List[String]): Map[Int, String]= {
val m = list.zipWithIndex.map(_.swap).toMap
val sM = SortedMap(m.toSeq:_*)
sM.+(0 -> "root")
}

After getting the data in the structure required, I loop through the entire collection to structure the data into a path map which would look like

root - twiki - bin - edit - Main - Double_bounce_sender -rdiff - TWiki - NewUserTemplate - oops - etc - local - getInterface

type innerMap = mutable.HashMap[String, Any]

def getData(input: RDD[List[String]]): mutable.HashMap[String, innerMap] ={
var mainMap = new mutable.HashMap[String, innerMap]
for(x <- input){
  val z: mutable.HashMap[String, innerMap] = storeData(x.toIterator, mainMap ,x(0).toString)
  mainMap = mainMap ++ z
}
mainMap
}

def storeData(list: Iterator[String], map: mutable.HashMap[String, innerMap], root: String): mutable.HashMap[String, innerMap]={
list.hasNext match {
  case true =>
    val v = list.next()
    val y = map contains (root) match {
      case true =>
        println("Adding when exists: "+v)
        val childMap = map.get(v).get match {
          case _:HashMap[String, Any] => asInstanceOf[mutable.HashMap[String, innerMap]]
          case _ => new mutable.HashMap[String, innerMap]
        }
        val x = map + (v -> storeData(list, childMap, v))
        x
      case false =>
        val x = map + (v -> storeData(list, new mutable.HashMap[String, innerMap], v))
        x
    }
    y.asInstanceOf[mutable.HashMap[String, innerMap]]
  case false =>
    new mutable.HashMap[String, innerMap]
    }
}

The get data method calls each input list and sends it to the storeData method which builds the map.

I'm stuck at two places.

  • The MainMap(HashMap[String, innerMap]) sent recursively to storeData goes as a new empty map every time.
  • The second issue is that I'm trying to figure out a way of merging 2 nested Maps that do not have a defined length. Such as merging the maps below.

Map(root -> Map(twiki -> Map(bin -> Map(edit -> Map(Main -> Map(Double -> Map()))))))) Map(root -> Map(twiki -> Map(bin -> Map(rdiff -> Map(TWiki -> Map(NewUser -> Map())))))))

Looking for suggestions on how I could implement this solution and get a final map that contains all the possible paths present in the server log files in one map.

2 Answers 2

2

to merge these two maps you can use scalaz and |+| method

@ Map("root" ->
    Map("twiki" ->
      Map("bin" ->
        Map("rdiff" ->
          Map("TWiki" ->
            Map("NewUser" ->
              Map.empty[String, String]))))))
res2: Map[String, Map[String, Map[String, Map[String, Map[String, Map[String, Map[String, String]]]]]]] =
  Map("root" ->
    Map("twiki" ->
      Map("bin" ->
        Map("rdiff" ->
          Map("TWiki" ->
            Map("NewUser" -> Map()))))))

@ Map("root" ->
    Map("twiki" ->
      Map("bin" ->
        Map("edit" ->
          Map("Main" ->
            Map("Double" ->  Map.empty[String, String]))))))
res3: Map[String, Map[String, Map[String, Map[String, Map[String, Map[String, Map[String, String]]]]]]] =
  Map("root" ->
    Map("twiki" ->
      Map("bin" ->
        Map("edit" ->
          Map("Main" ->
            Map("Double" -> Map()))))))

res2 |+| res3
res4: Map[String, Map[String, Map[String, Map[String, Map[String, Map[String, Map[String, String]]]]]]] =
  Map("root" ->
    Map("twiki" ->
      Map("bin" ->
        Map(
          "edit" ->
            Map("Main" ->
              Map("Double" -> Map())),
          "rdiff" ->
            Map("TWiki" ->
              Map("NewUser" -> Map()))))))
Sign up to request clarification or add additional context in comments.

4 Comments

I will not be able to be able to use the |+| method when the you use a collection.mutable.HashMap or even a collection.immutable.Map[String, Any]
Maps need to be of the same type, your maps from example are so I asumed that. I don't have a simple solution for any maps. Btw, try to read what you are posting as this is clearly not in English.
My Maps are of the same type, but scalaz doesn't allow you to implement the |+| method on a mutable map collection.mutable.HashMap. This is the error I get error: value |+| is not a member of scala.collection.immutable.Map[String,Any]. I would either have to change the collection i'm using to a Map type or write some logic to do exactly what the |+| method does but to a mutable map.
yes it is not a member of Map[String, Any] because it doesn't know how to merge Any with Any. If any of nested maps has elements of more than one type I don't know how to merge it easly.
0

Maybe something like this?

scala>   type Node = Map[String, Any];
defined type alias Node

scala>   def merge( me : Node, you : Node ) : Node = {
     |     val keySet = me.keySet ++ you.keySet;
     |     def nodeForKey( parent : Node, key : String ) : Node = parent.getOrElse( key, Map.empty ).asInstanceOf[Node]
     |     keySet.map( key => (key -> merge( nodeForKey( me, key ), nodeForKey( you, key ) ) ) ).toMap
     |   }
merge: (me: Node, you: Node)Node

scala> val path1 = Map( "root" -> Map("bin" -> Map("sh" -> Map.empty) ) )
path1: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,scala.collection.immutable.Map[Nothing,Nothing]]]] = Map(root -> Map(bin -> Map(sh -> Map())))

scala> val path2 = Map( "root" -> Map( "bin" -> Map("csh" -> Map.empty), "usr" -> Map.empty ) )
path2: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,scala.collection.immutable.Map[_ <: String, scala.collection.immutable.Map[Nothing,Nothing]]]] = Map(root -> Map(bin -> Map(csh -> Map()), usr -> Map()))

scala> merge( path1, path2 )
res8: Node = Map(root -> Map(bin -> Map(sh -> Map(), csh -> Map()), usr -> Map()))

1 Comment

This solution might work, but the required output would have to look a little different. According to your solution above the out put would be Map(root -> Map(bin -> Map(sh -> Map(), csh -> Map()), usr -> Map())), but it should be Map(root -> Map(bin -> Map(sh -> Map(), csh -> Map(usr -> Map()))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.