I have a Map which has a key containing another Map.
i.e -
val myDetailsMap = Map("name" -> "abc",
"class" -> "10",
"section" -> "A",
"marksPerSubjectId" -> Map(101 -> "Physics= '70' AND Chemistry='80'",
102 -> "History= '60' AND Civics = '67'"),
"status" -> "pass")
Now, I want to iterate through the marksPerSubjectId key containing another MAP using foreach. How should I proceed ?
On Databricks -
What about using pattern matching?
in Scala 2.13:
myDetailsMap.foreachEntry{ (k, v) =>
v match {
case map: Map[_, _] => map.foreachEntry{ (k, v) => println(v)}
case other => println(other)
}
}
in Scala 2.11:
myDetailsMap.foreach{ case (k, v) =>
v match {
case map: Map[_, _] => map.foreach{ case (k, v) => println(v)}
case other => println(other)
}
}
Related
This question already has answers here:
Merge maps by key
(8 answers)
Closed 5 years ago.
I have 2 Maps :
val map1 = Map("col_1" -> "data_1", "col_2" -> "data_2", "col_3" -> "data_3")
val map2 = Map("col_1" -> "myval_1", "col_2" -> "myval_2", "col_3" -> "myval_3")
Required Output:
res = Map("col_1" -> ("data_1", "myval_1"), "col_2" -> ("data_2", "myval_2"),
"col_2" -> ("data_2", "myval_2") )
Basically Keeping the keys of 'map1' & merging values of both maps
Output must be Tuple and not a List or Seq
Use map (throws if one of keys is missing on the other map):
val res = map1.map { case (k, v) => (k, (v, map2(k))) }
Or use collect (skips the keys not present in both maps):
val res = map1.collect { case (k, v) if map2.contains(k) => (k, (v, map2(k))) }
Or with default value for map2:
val res = map1.map { case (k, v) => (k, (v, map2.getOrElse(k, ""))) }
For symmetric case, I'd go with Scalaz version from my other answer
Get all the keys which present in map1 along with the combination which keys present in map2 :
val res = map1.collect { case (k, v) => if (map2.contains(k)) (k, (v, map2(k))) else (k, (v, "")) }
This is a simple solution you can apply in your case
val resultMap = map1.map(kv => {
if(map2(kv._1) != None){
kv._1 -> (kv._2, map2(kv._1))
}
else{
kv
}
})
resultMap should be
Map(col_1 -> (data_1,myval_1), col_2 -> (data_2,myval_2), col_3 -> (data_3,myval_3))
Above case fails when there is no key of map1 in map2, for that case you can use match case as following
val resultMap = map1.map(kv => map2 getOrElse(kv._1, "noKey") match{
case "noKey" => kv
case x => kv._1 -> (kv._2, x)
})
My program receives a scala map, the requirements is to validate this map (key-value pairs). Ex: validate a key value, change its value to an acceptable format etc. In a rare case, we update the key as well before passing the map to the down layer. Its not always required to update this map , but only when we detect that there are any unsupported keys or values. However, we have to check all key/value pairs. I'm doing some thing like this:
private def updateMap ( parameters: Map[String, String]): Map[String, String] = {
parameters.map{
case(k,v) => k match { case "checkPool" =>
(k, (if (k.contains("checkPool"))
v match {
case "1" => "true"
case _ => "false"
}
else v))
case "Newheader" => (k.replace("Newheader","header"),v)
case _ =>(k,v)
}
case _ => ("","")
}
}
Like this the code increases for doing the validation and converting the keys/values to supported ones. Is there a cleaner way of doing this validation in Scala for a map?
Thanks
It will be clearer if you put all your patterns above one another:
parameters.map{
case (k#"checkPool", "1") => k -> "true"
case (k#"checkPool", _") => k -> "false"
case ("Newheader", v) => "header" -> v
// put here all your other cases
case (k, v) => k -> v //last possible case, if nothing other matches
}
For clarity, you can also put different validators in partial functions:
type Validator = PartialFunction[(String, String), (String, String)
val checkPool: Validator = {
case (k#"checkPool", "1") => k -> "true"
case (k#"checkPool", _") => k -> "false"
}
val headers: Validator = {
case ("Newheader", v) => "header" -> v
}
And then put all your validators one after the other in your map:
parameters.map(
checkPool orElse
headers orElse
... orElse
PartialFunction(identity[(String, String)]) //this is the same as case (k, v) => k -> v
)
simple if else condition matching seems to be the best choice.
def updateMap(parameters: Map[String, String]): Map[String, String] = {
parameters.map(kv => {
var key = kv._1
var value = kv._2
if(key.contains("checkPool")){
value = if(value.equals("1")) "true" else "false"
}
else if(key.contains("Newheader")){
key = key.replace("Newheader", "header")
}
(key, value)
})
}
You can add more else if conditions
I have a method as follows:
protected def extract(implicit params:Params) =
Map(
"address" -> params.address,
"city" -> params.address,
"region" -> params.region,
)collect {
case (k, v) if v.isDefined => k -> v.get
}
I want to substitute value of city such that :
"city" -> if(params.city.contains("York")) "NewYork" else params.city,
How can I achieve this in scala?
You could put it in line
def extract(implicit params:Params) =
Map(
"address" -> params.address,
"city" -> (if(params.city.contains("York")) Some("NewYork") else params.city),
"region" -> params.region
) collect {
case (k, v) if v.isDefined => k -> v.get
}
Im sure there are lots of other way to do it. Not sure what your really after.
Stephens approach of just creating the map with the correct value is probably best. If you've got a general map, of what appears to be String to Option[String], and want to substitute the city key if the value contains York, then this will work as well.
myMap.collect {
case ("city", Some(city)) if(city.contains("York")) => "NewYork"
case (k, v) if v.isDefined => k -> v.get
}
Small correction to Bruce answer
myMap.collect {
case ("city", Some(city)) if(city.contains("York")) => "NewYork"
case (k, v) if v.isDefined => k -> v.get
}
Would not result in a Map, but an Iterable
myMap.collect {
case ("city", Some(city)) if(city.contains("York")) => "city" -> "NewYork"
case (k, Some(v)) => k -> v
}
Would be better
I am struggling with finding an elegant FP approach to solving the following problem in Scala:
Say I have a set of candidate keys
val validKeys = Set("key1", "key2", "key3")
And a list that
Starts with a key
has some number of non-keys (> 0) between each key
Does not end with a key
For example:
val myList = List("key3", "foo", "bar", "key1", "baz")
I'd like to transform this list into a map by choosing using valid keys as the key and aggregating non-keys as the value. So, in the example above:
("key3" -> "foo\nbar", "key1" -> "baz")
Thanks in advance.
Short and simple:
def create(a: List[String]): Map[String, String] = a match {
case Nil => Map()
case head :: tail =>
val (vals, rest) = tail.span(!validKeys(_))
create(rest) + (head -> vals.mkString("\n"))
}
Traversing a list from left to right, accumulating a result should suggest foldLeft
myList.foldLeft((Map[String, String](), "")) {
case ((m, lk), s) =>
if (validKeys contains s)
(m updated (s, ""), s)
else (m updated (lk, if (m(lk) == "") s else m(lk) + "\n" + s), lk)
}._1
// Map(key3 -> foo\nbar, key1 -> baz)
As a first approximation solution:
def group(list:List[String]):List[(String, List[String])] = {
#tailrec
def grp(list:List[String], key:String, acc:List[String]):List[(String, List[String])] =
list match {
case Nil => List((key, acc.reverse))
case x :: xs if validKeys(x) => (key, acc.reverse)::group(x::xs)
case x :: xs => grp(xs, key, x::acc)
}
list match {
case Nil => Nil
case x::xs => grp(xs, x, List())
}
}
val map = group(myList).toMap
Another option:
list.foldLeft((Map[String, String](), "")) {
case ((map, key), item) if validKeys(item) => (map, item)
case ((map, key), item) =>
(map.updated(key, map.get(key).map(v => v + "\n" + item).getOrElse(item)), key)
}._1
I got the following list of pairs:
List(("US","New York"),("England","London"),("US","Los Angeles"),("England","Manchester"),("US","Washington"))
I need to generate a Map[Country, List[Cities]]:
Map("US" -> List("New York", "Los Angeles", "Washington"), "England" -> List("London", "Manchester"))
The problem that if I use toMap() directly the values with same keys are removed.
The story so far:
list.groupBy(el => el).map(el => el._1 -> ?)
using groupBy:
list.groupBy(_._1).mapValues(_.map(_._2))
using fold:
list.foldLeft(Map.empty[String, List[String]]) { case (m, (k, v)) =>
m.updated(k, v :: m.getOrElse(k, List()))
}