Consider var myMap = Map[String,Map[String,Map[String,String]]]().
1) I tried to add entries to this variable as shown below but was unsuccessful: myMap = myMap + ("a" -> ("b1" -> ("c" -> "d", "e" -> "f"))) How may I fix that?
2) Assuming we are done with step 1 above, how can we add add another sub-map somewhere in the structure; say myMap = myMap + ("a" -> ("b2" -> ("g" -> "h")))?
The final result should be something similar to the structure below:
a:{
b1:{
c:d,
e:f
},
b2:{
g:h
}
}
This is going to be easier to do with a mutable collection rather than a mutable variable.
import collection.mutable.Map
val myMap = Map[String,Map[String,Map[String,String]]]()
myMap.update("a", Map("b1" -> Map("c" -> "d", "e" -> "f")))
myMap("a").update("b2", Map("g" -> "h"))
//Map(a -> Map(b2 -> Map(g -> h), b1 -> Map(c -> d, e -> f)))
Related
So I have a list of maps like this
val data = List(
Map[String, String]("name" -> "Bob", "food" -> "pizza", "day" -> "monday"),
Map[String, String]("name" -> "Ron", "food" -> "hotdog", "day" -> "tuesday"),
Map[String, String]("name" -> "Tim", "food" -> "pizza", "day" -> "wednesday"),
Map[String, String]("name" -> "Carl", "food" -> "hotdog", "day" -> "wednesday")
)
I want to make a Map like this from that List of maps
val result = Map("pizza" -> Map("name" -> ("Bob", "Tim"), "day" -> ("monday", "wednesday")),
"hotdog"-> Map("name" -> ("Ron", "Carl"), "day" -> ("tuesday", "wednesday")))
How can I achieve this result? Thanks
*ps I'm a beginner in Scala
Here is a preliminary solution, there is probably an easier way of doing this with fold but I have to sketch that out separately
data.groupMap(a => a("food"))(_.filter(_._1 != "food"))
.map{
case (a,b) =>
(a, b.flatten.groupMapReduce(_._1)(a => List(a._2))(_ ++ _))}
You group the maps inside based on the value of food
This gives you:
Map(
hotdog -> List(
Map(name -> Ron, food -> hotdog, day -> tuesday),
Map(name -> Carl, food -> hotdog, day -> wednesday)),
pizza -> List(
Map(name -> Bob, food -> pizza, day -> monday),
Map(name -> Tim, food -> pizza, day -> wednesday))
)
You remove the key food from the inner maps
Map(
hotdog -> List(
Map(name -> Ron, day -> tuesday),
Map(name -> Carl, day -> wednesday)),
pizza -> List(
Map(name -> Bob, day -> monday),
Map(name -> Tim, day -> wednesday))
)
You "merge" the maps inside by using groupMapReduce which
a) groups by the inner key (i.e. name and day)
b) maps each value to a singleton list
c) concats the lists
Edit: Here is a single pass solution using foldLeft but I don't think I like this any better. All the key accesses are unsafe and will blow up if your entry is missing the key. So ideally you would need to use .get() to get back an option and do bunch of pattern matching
data.foldLeft(Map[String, Map[String, List[String]]]())((b, a) => {
val foodVal = a("food")
b.get(foodVal) match{
case None => b + (foodVal ->
List("name" -> List(a("name")), "day" -> List(a("day"))).toMap)
case Some(v : Map[String, List[String]]) =>
b + (foodVal ->
List("name" -> (v("name") :+ a("name")), "day" -> (v("day") :+ a("day"))).toMap)
}
})
I have something like this:
val m1 = Map(A -> List(("a","b"),("c","d"),("e","f")))
I want the result to be :
(Map(A -> List("a","b")), Map(A -> List ("c","d")), Map(A -> List("e","f")))
could anyone help?
Thanks
It's hard to tell what exactly you are trying to do, but here is a way to convert m1 to the desired structure:
val m1 = Map("A" -> List(("a","b"),("c","d"),("e","f")))
m1.toList.flatMap { case (key, tuple) =>
tuple.map(v => Map(key -> List(v._1, v._2)))
}
I have a map like :
val programming = Map(("functional", 1) -> "scala", ("functional", 2) -> "perl", ("orientedObject", 1) -> "java", ("orientedObject", 2) -> "C++")
with the same first element of key appearing multiple times.
How to regroup all the values corresponding to the same first element of key ? Which would turn this map into :
Map("functional" -> List("scala","perl"), "orientedObject" -> List("java","C++"))
UPDATE: This answer is based upon your original question. If you need the more complex Map definition, using a tuple as the key, then the other answers will address your requirements. You may still find this approach simpler.
As has been pointed out, you can't actually have multiple keys with the same value in a map. In the REPL, you'll note that your declaration becomes:
scala> val programming = Map("functional" -> "scala", "functional" -> "perl", "orientedObject" -> "java", "orientedObject" -> "C++")
programming: scala.collection.immutable.Map[String,String] = Map(functional -> perl, orientedObject -> C++)
So you end up missing some values. If you make this a List instead, you can get what you want as follows:
scala> val programming = List("functional" -> "scala", "functional" -> "perl", "orientedObject" -> "java", "orientedObject" -> "C++")
programming: List[(String, String)] = List((functional,scala), (functional,perl), (orientedObject,java), (orientedObject,C++))
scala> programming.groupBy(_._1).map(p => p._1 -> p._2.map(_._2)).toMap
res0: scala.collection.immutable.Map[String,List[String]] = Map(functional -> List(scala, perl), orientedObject -> List(java, C++))
Based on your edit, you have a data structure that looks something like this
val programming = Map(("functional", 1) -> "scala", ("functional", 2) -> "perl",
("orientedObject", 1) -> "java", ("orientedObject", 2) -> "C++")
and you want to scrap the numerical indices and group by the string key. Fortunately, Scala provides a built-in that gets you close.
programming groupBy { case ((k, _), _) => k }
This will return a new map which contains submaps of the original, grouped by the key that we return from the "partial" function. But we want a map of lists, so let's ignore the keys in the submaps.
programming groupBy { case ((k, _), _) => k } mapValues { _.values }
This gets us a map of... some kind of Iterable. But we really want lists, so let's take the final step and convert to a list.
programming groupBy { case ((k, _), _) => k } mapValues { _.values.toList }
You should try the .groupBy method
programming.groupBy(_._1._1)
and you will get
scala> programming.groupBy(_._1._1)
res1: scala.collection.immutable.Map[String,scala.collection.immutable.Map[(String, Int),String]] = Map(functional -> Map((functional,1) -> scala, (functional,2) -> perl), orientedObject -> Map((orientedObject,1) -> java, (orientedObject,2) -> C++))
you can now "clean" by doing something like:
scala> res1.mapValues(m => m.values.toList)
res3: scala.collection.immutable.Map[String,List[String]] = Map(functional -> List(scala, perl), orientedObject -> List(java, C++))
Read the csv file and create a map that contains key and list of values.
val fileStream = getClass.getResourceAsStream("/keyvaluepair.csv")
val lines = Source.fromInputStream(fileStream).getLines
var mp = Seq[List[(String, String)]]();
var codeMap=List[(String, String)]();
var res = Map[String,List[String]]();
for(line <- lines )
{
val cols=line.split(",").map(_.trim())
codeMap ++= Map(cols(0)->cols(1))
}
res = codeMap.groupBy(_._1).map(p => p._1 -> p._2.map(_._2)).toMap
Since no one has put in the specific ordering he asked for:
programming.groupBy(_._1._1)
.mapValues(_.toSeq.map { case ((t, i), l) => (i, l) }.sortBy(_._1).map(_._2))
For exemple:
mylist: Map("Start" -> 2015-05-30T00:00:00.000Z, "Daily" -> 2015-06-02T00:00:00.000Z, "Hourly" -> 2015-06-03T08:00:00.000Z, "End" -> 2015-06-04T15:00:00.000Z)
I want to output as following format:
myout: List( ("Start" -> 2015-05-30T00:00:00.000Z, "Daily" -> 2015-06-02T00:00:00.000Z), ("Daily" -> 2015-06-02T00:00:00.000Z, "Hourly" -> 2015-06-03T08:00:00.000Z), ("Hourly" -> 2015-06-03T08:00:00.000Z, "End" -> 2015-06-04T15:00:00.000) )
OR
myout: List( ("Start", "Daily"), ("Daily", "Hourly"), ("Hourly", "End"))
Case 1: Always start with "Start" key, Anything comes before "Start" key ignore it. Same for last "End" key
mylist: Map(Hourly -> 2015-06-01T08:00:00.000Z, Start -> 2015-05-30T00:00:00.000Z, Daily -> 2015-06-02T00:00:00.000Z, End -> 2015-06-04T15:00:00.000Z, Weekly-> 2015-06-05T00:00:00.000Z)
output should like:
List((Start, Daily), (Daily, End))
I am looking output using scala.
import scala.collection.immutable.ListMap
val x = ListMap("Start" -> "x", "Daily" -> "y", "Hourly" -> "z", "End" -> "a")
x.toList.sliding(2).map( a => (a(0)._1, a(1)._1)).toList
List((Start,Daily), (Daily,Hourly), (Hourly,End))
Since a Map is not ordered, I have modified the input data to get stable results.
As for the 1st question
val m =
Map(
"1-Start" -> "2015-05-30T00:00:00.000Z",
"2-Daily" -> "2015-06-02T00:00:00.000Z",
"3-Hourly" -> "2015-06-03T08:00:00.000Z",
"4-End" -> "2015-06-04T15:00:00.000Z")
The basic idea is to zip the list of keys with its own tail to get the pairs:
scala> m.keys.toList.sorted.zip(m.keys.toList.sorted.tail)
res57: List[(String, String)] = List((1-Start,2-Daily), (2-Daily,3-Hourly),
(3-Hourly,4-End))
To simplify the expression a "pipe forward operator" is helpful:
object PipeForwardContainer {
implicit class PipeForward[T](val v: T) extends AnyVal {
def |>[R](f: T => R): R = {
f(v)
}
}
}
import PipeForwardContainer._
This operator provides a reference to the intermediate result. Therefore you can write:
scala> m.keys.toList.sorted |> { l => l.zip(l.tail) }
res97: List[(String, String)] = List((1-Start,2-Daily), (2-Daily,3-Hourly),
(3-Hourly,4-End))
As for the 2nd question
val m =
Map(
"1-Hourly" -> "2015-06-03T08:00:00.000Z",
"2-Start" -> "2015-05-30T00:00:00.000Z",
"3-Daily" -> "2015-06-02T00:00:00.000Z",
"4-End" -> "2015-06-04T15:00:00.000Z",
"5-Weekly"-> "2015-06-05T00:00:00.000Z")
To get the raw list you can slice out the relevant elements by index:
scala> m.keys.toList.sorted |> { l =>
l.slice(l.indexOf("2-Start"), l.indexOf("4-End") + 1) }
res96: List[String] = List(2-Start, 3-Daily, 4-End)
Again with zip to get the pairs:
scala> m.keys.toList.sorted |> { l =>
l.slice(l.indexOf("2-Start"), l.indexOf("4-End") + 1)
} |> { l => l.zip(l.tail) }
res98: List[(String, String)] = List((1-Start,2-Daily), (2-Daily,3-Hourly),
(3-Hourly,4-End))
I query data from multiple tables, and each has a customized key. I put the data from these tables into a list of maps and want to sort it by the id value.
What I end up with is:
var g = groups.map(i => Map("id" -> i._1, "job" -> i._2))
var p = people.map(i => Map("id" -> i._1, "job" -> i._2))
var party = g ++ p
Which gives me:
var party = List(
Map(id -> 1, job -> Group1),
Map(id -> 2, job -> Group2),
Map(id -> 2>1, job -> Person1Group2)
Map(id -> 1>1, job -> Person1Group1),
Map(id -> 1>2, job -> Person2Group1)
)
But I want to sort by id so that I have it in an order so that i can populate a tree structure:
var party = List(
Map(id -> 1, job -> Group1),
Map(id -> 1>1, job -> Person1Group1),
Map(id -> 1>2, job -> Person2Group1),
Map(id -> 2, job -> Group2),
Map(id -> 2>1, job -> Person1Group2)
)
How do I do this?
A minor refactoring of the associations in each Map by using case classes may simplify the subsequent coding; consider
case class Item(id: String, job: String)
and so by using (immutable) values,
val g = groups.map(i => Item(i._1, i._2)
val p = people.map(i => Item(i._1, i._2)
Then
(g ++ p).sortBy(_.id)
brings a list of items sorted by id.
If you wish to group jobs by id, consider
(g ++ p).groupBy(_.id)
which delivers a Map from ids onto lists of items with common id. From this Map you can use mapValues to extract the actual jobs.
as hinted above party.sortBy(_("id")) should do it