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.
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()))))))
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()))
Related
input:
type DeviceType = Any
val rh: Map[(String, String), String] = Map(("ss", "ex-ss") -> "MobileType", ("ud", "ex-ud") -> "DesktopType")
val lh: Map[String, DeviceType] = Map("ex-ss" -> 1, "ex-ud" -> "A#F4CC", "ex-zip" -> 30052)
Expected output:
val res:Map[String, DeviceType] = Map("ss" -> 1, "ud" -> "A#F4CC", "ex-zip" -> 30052)
So, basically you would need to create one more map to store connection between the tuples, from rh map.
Please, see code below for more details:
// Create device synonyms index, for instance: "ex-ss" -> "ss"
val deviceSynonyms: Map[String, String] = rh.keys.map(_.swap).toMap
val res: Map[String, DeviceType] = lh.map {
case (deviceName, deviceType) => deviceSynonyms.getOrElse(deviceName, deviceName) -> deviceType
}
Which will produce desired result:
Map(ss -> 1, ud -> A#F4CC, ex-zip -> 30052)
The solution is :
type DeviceType = Any
val rh: Map[(String, String), String] = Map(("ss", "ex-ss") -> "MobileType", ("ud", "ex-ud") -> "DesktopType")
val lh: Map[String, DeviceType] = Map("ex-ss" -> 1, "ex-ud" -> "A#F4CC", "ex-zip" -> 30052)
val rhKeySet = rh.keySet
val res: Map[String, DeviceType] = lh.map {
case (k,v) =>
if (rh.exists(_._1._2 == k)) {
val res = rhKeySet.filter(_._2 == k).head
res._1 -> v
}else{
k -> v
}
}
The output:
Map(ss -> 1, ud -> A#F4CC, ex-zip -> 30052)
Let me know if it helps!!
Suppose I have two Maps like these
Map(("a" -> "x-100"), ("b" -> "x-200"), ("c" -> "x-300"))
Map(("a" -> "y-100"), ("b" -> "y-200"), ("c" -> "y-300"))
What would be the simplest way to transform them into the following (assuming that all the values are unique and with possibly different lengths)?
Map(("x-100" -> "y-100"), ("x-200" -> "y-200"), ("x-300" -> "y-300"))
Or with a for comprehension and some safety:
for{
(k, v1) <- m1
v2 = m2.get(k)
if (v2.isDefined)
} yield (v1 -> v2.get)
This returns Map(x-100 -> y-100, x-200 -> y-200, x-300 -> y-300)
Check ScalaFiddle
val m1 = Map(("a" -> "x-100"), ("b" -> "x-200"), ("c" -> "x-300"),("d" -> "ignored"))
val m2 = Map(("a" -> "y-100"), ("b" -> "y-200"), ("c" -> "y-300"))
m1.keySet.intersect(m2.keySet).map(k=>m1(k)->m2(k)).toMap
You can do it like this:
val a = Map(("a" -> "x-100"), ("b" -> "x-200"), ("c" -> "x-300"))
val b = Map(("a" -> "y-100"), ("b" -> "y-200"), ("c" -> "y-300"))
def zipMaps(map1: Map[String, String], map2: Map[String, String]) = {
for(key <- map1.keys ++ map2.keys)
yield (key, map1.get(key), map2.get(key))
}
val result = zipMaps(a,b).map{
case (k,Some(v1),Some(v2)) => (v1,v2)
case (k,_ ,_)=> ("", "")
}.toMap.filterKeys(_ != "")
// result = Map(x-100 -> y-100, x-200 -> y-200, x-300 -> y-300)
val a = Map("a" -> "x-100", "b" -> "x-200", "c" -> "x-300")
val b = Map("a" -> "y-100", "b" -> "y-200", "c" -> "y-300")
val c = a.map {
case (k, v) => v -> b(k)
}
println(c) // Map(x-100 -> y-100, x-200 -> y-200, x-300 -> y-300)
I need to get an object User(1,Nick Holland,25,None) from the List(Map()), but I can't understand how
val a = request.body.asFormUrlEncoded.toSeq.map(a => a.map(b => b))
List(Map(name -> ArrayBuffer(), age -> ArrayBuffer(), deleteItem -> ArrayBuffer(User(1,Nick Holland,25,None)), action -> ArrayBuffer(remove)))
Try
case class User(i: Int, str: String, i1: Int, opt: Option[String])
val l = List(Map("name" -> ArrayBuffer(), "age" -> ArrayBuffer(), "deleteItem" -> ArrayBuffer(User(1,"Nick Holland",25,None)), "action" -> ArrayBuffer("remove")))
l.head.apply("deleteItem").head //User(1,Nick Holland,25,None)
I have a List of Maps like List(map1, map2, map3)
And another Map(String, String) say.. Map(String,String) specified as outer in below example :
I want the keys of the Map named 'outer' as key and map it to m1 map within the List. That means i want a 1-to-1 mapping between keys of outer map with the maps in the List
val m1 = Map("id"->"int", "firstname"->"String", "lastname"->"String")
val m2 = Map("Address"->"String", "Salary"->"Int")
val m3 = Map("Mobile" -> "Int", "email"->"String", "landline"->"int")
val listMap = List(m1,m2,m3)
val outer = Map("test1" -> "location_1", "test2" -> "location_2", "test3" -> "location_3")
val res = outer.map(out => listMap.map(inner => (out._1,inner)))
res.foreach(println)
This generated the Output as : (which i don't want)
List((test1,Map(id -> int, firstname -> String, lastname -> String)), (test1,Map(Address -> String, Salary -> Int)), (test1,Map(Mobile -> Int, email -> String, landline -> int)))
List((test2,Map(id -> int, firstname -> String, lastname -> String)), (test2,Map(Address -> String, Salary -> Int)), (test2,Map(Mobile -> Int, email -> String, landline -> int)))
List((test3,Map(id -> int, firstname -> String, lastname -> String)), (test3,Map(Address -> String, Salary -> Int)), (test3,Map(Mobile -> Int, email -> String, landline -> int)))
res0: Unit = ()
What i want is :
Map(test1 -> map1, test2 -> map2, test3 -> map3)
How can i achieve this ..??
Maps don't retain order, so I don't think this is possible with the setup you have. How about this?:
val output: Map[String, Map[String, String]] = listMap.zipWithIndex
.map { case (map, i) => s"test${i + 1}" -> map } (collection.breakOut)
I was able to achieve this by :
val newMap = outer.zipWithIndex.map{
x => (x._1._1, listMap.zipWithIndex.apply(x._2)._1)
}
newMap.map(println)
Can someone recommend a functional way to transform the map specified below from
Map("host.config.autoStart.powerInfo[1].startOrder" -> -1,
"host.config.autoStart.powerInfo[1].startAction" -> "None",
"host.config.autoStart.powerInfo[1].key" -> "vm-XXX",
"host.config.autoStart.powerInfo[0].key" -> "vm-YYY",
"host.config.autoStart.powerInfo[0].startOrder" -> -1,
"host.config.autoStart.powerInfo[0].startAction" -> "None")
to
Map("host.config.autoStart.powerInfo" -> Map(
1 -> Map("startOrder" -> -1,
"startAction" -> "None",
"key" -> "vm-639"),
0 -> Map("startOrder" -> -1,
"startAction" -> "None",
"key" -> "vm-641")))
Extract what is before the subscript and make that the key
Extract the number between the subscript [x] and make that the key of value
A one line (long) solution:
val R = """([^\[]+)\[(\d+)\]\.(.+)""".r
m.map{ case(R(h,i,k),v) => (h,i,k,v) }.groupBy(_._1).mapValues(_.groupBy(_._2).mapValues{ _.map{case(h,i,k,v) => (k,v)}.toMap} )
res1: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,Any]]] =
Map(host.config.autoStart.powerInfo ->
Map(1 -> Map(startAction -> None,
startOrder -> -1,
key -> vm-XXX),
0 -> Map(key -> vm-YYY,
startAction -> None,
startOrder -> -1)
))
Or write it more or less readable:
m.map{ case(R(h,i,k),v) => (h,i,k,v) }
.groupBy(_._1).mapValues{ value =>
value.groupBy(_._2).mapValues{ _.map{case(h,i,k,v) => (k,v)}.toMap}
}
Edit: added some comments to the code to make it easier to see what's going on
Copied from my REPL:
scala> val re = """(.+)\[(\d+)\]\.(.+)""".r // Creates a regex to grab the key values
re: scala.util.matching.Regex = (.+)\[(\d+)\]\.(.+)
scala> val m = Map("host.config.autoStart.powerInfo[1].startOrder" -> -1,"host.config.autoStart.powerInfo[1].startAction" -> "None","host.config.autoStart.powerInfo[1].key" -> "vm-XXX","host.config.autoStart.powerInfo[0].key" -> "vm-YYY","host.config.autoStart.powerInfo[0].startOrder" -> -1,"host.config.autoStart.powerInfo[0].startAction" -> "None")
m: scala.collection.immutable.Map[String,Any] = Map(host.config.autoStart.powerInfo[0].key -> vm-YYY, host.config.autoStart.powerInfo[0].startAction -> None, host.config.autoStart.powerInfo[0].startOrder -> -1, host.config.autoStart.powerInfo[1].startAction -> None, host.config.autoStart.powerInfo[1].startOrder -> -1, host.config.autoStart.powerInfo[1].key -> vm-XXX)
scala> val tempList = m map { // Construct a temporary list of Tuples with all relevant values
| case (key, value) => key match {
| case re(p, i, k) => (p, i, k, value)
| }}
tempList: scala.collection.immutable.Iterable[(String, String, String, Any)] = List((host.config.autoStart.powerInfo,0,key,vm-YYY), (host.config.autoStart.powerInfo,0,startAction,None), (host.config.autoStart.powerInfo,0,startOrder,-1), (host.config.autoStart.powerInfo,1,startAction,None), (host.config.autoStart.powerInfo,1,startOrder,-1), (host.config.autoStart.powerInfo,1,key,vm-XXX))
scala> val accumulator = Map[String, Map[String, Map[String, Any]]]()
accumulator: scala.collection.immutable.Map[String,Map[String,Map[String,Any]]] = Map()
scala> val result = tempList.foldLeft(accumulator) {
| case (acc, e) => {
| val middleMap = acc.getOrElse(e._1, Map[String, Map[String, Any]]())
| val innerMap = middleMap.getOrElse(e._2, Map[String, Any]())
| acc + (e._1 -> (middleMap + (e._2 -> (innerMap + (e._3 -> e._4)))))
| }}
result: scala.collection.immutable.Map[String,Map[String,Map[String,Any]]] = Map(host.config.autoStart.powerInfo -> Map(0 -> Map(key -> vm-YYY, startAction -> None, startOrder -> -1), 1 -> Map(startAction -> None, startOrder -> -1, key -> vm-XXX)))