Scala merge list of maps - scala

Input =
val data = List(
Map("id" -> "123", "name" -> "raju",
"sub" -> List(Map("id" -> "english", "status" -> 2))),
Map("id" -> "123", "name" -> "raju",
"sub" -> List(Map("id" -> "english", "status" -> 0),
Map("id" -> "hindi", "status" -> 0))))
Expected Output:
val data = List(
Map("id" -> "123", "name" -> "raju",
"sub" -> List(Map("id" -> "english", "status" -> 2),
Map("id" -> "hindi", "status" -> 1),
Map("id" -> "english", "status" -> 0),
Map("id" -> "hindi", "status" -> 0))))
I'm trying to merge the "sub" key values into a list without removing the old keys but I couldn't able to get it. can anyone please help with this using scala.

Start by creating some case classes. It is much easier to work with these than with maps.
case class Person(id: String, name: String, sub: List[Language])
case class Language(id: String, status: Int)
Now, create a list of Person:
val data = List(
Person("123", "raju", List(Language("english", 2))),
Person("123", "raju", List(Language("english", 0), Language("hindi", 0))))
I don't know what your merging logic is, but let's say you just want to concatenate the sub-lists for persons with the same ID. You could do it this way:
// Gets a map from ID to the list of persons with that ID
data.groupBy(_.id)
// Gets a List[List[Person]] (the values of the map)
.values
// Makes a new Person out of the inner list of persons, concatenating all the languages of that person.
.map(persons => Person(
persons.head.id,
persons.head.name,
persons.flatMap(_.sub)))

Here is example that works on pure Maps/Lists like in your example.
val data = List(
Map("id" -> "123", "name" -> "raju",
"sub" -> List(Map("id" -> "english", "status" -> 2))),
Map("id" -> "123", "name" -> "raju",
"sub" -> List(Map("id" -> "english", "status" -> 0),
Map("id" -> "hindi", "status" -> 0))))
val expected = List(
Map("id" -> "123", "name" -> "raju",
"sub" -> List(Map("id" -> "english", "status" -> 2),
Map("id" -> "hindi", "status" -> 1),
Map("id" -> "english", "status" -> 0),
Map("id" -> "hindi", "status" -> 0))))
val res = data.groupBy(x => (x.get("id"), x.get("name"))).collect { case (_, items) =>
items.reduceLeft { (l, r) =>
l.updated("sub", l("sub").asInstanceOf[List[_]] ++ r("sub").asInstanceOf[List[_]])
}
}
println(res)
https://scalafiddle.io/sf/FBj4Mvi/0
PS: To be honest creating typed structure (as proposed by others) is probably still better sollution.

Related

Filter out empty Map from a Seq[Map]

I have this Seq[Map[String,String]] :
Seq(Map("Name"-> "Laura", "City" -> "Paris"), Map("Country" -> "Italy"), Map())
I want to remove the empty Map so I can obtain:
Seq(Map("Name"-> "Laura", "City" -> "Paris"), Map("Country" -> "Italy"))
How do I do that please?
val data = Seq(Map("Name"-> "Laura", "City" -> "Paris"), Map("Country" -> "Italy"), Map())
val dataWithoutEmptyMaps = data.filter(_.nonEmpty)

Parse nested maps in a function (gatling)

I have a map like this:
{
"user":
{
"name": "Jon Doe",
"age": "6",
"birthdate": {
"timestamp": 1456424096
},
"gender": "M"
}
}
and a function like this
def setUser(user: Map[String, Any]): Map[String, Any]={
var usr = Map("name"-> user.get("name").getOrElse(""),
"gender" -> user.get("gender").getOrElse(""),
"age" -> user.get("age").getOrElse(""),
"birthday" -> patient.get("birthdate"))
return usr
}
And I want to have the value of "timestamp" (1456424096) mapped in the "birthday" field.
For now I have this : Some%28%7Btimestamp%3D1456424096%7D%29
I'm very new to this. Can someone help me get the value of "timestamp"?
Assuming that you want just to get rid of nested birthday (not nested user) it can look like that:
val oldData: Map[String, Any] = Map(
"user" -> Map(
"name" -> "John Doe",
"age" -> 6,
"birthday" -> Map("timestamp" -> 1234454666),
"gender" -> "M"
)
)
def flattenBirthday(userMap: Map[String, Any]) = Map(
"user" -> (userMap("user").asInstanceOf[Map[String, Any]] + (
"birthday" -> userMap("user").asInstanceOf[Map[String, Any]]("birthday").asInstanceOf[Map[String, Any]]("timestamp")
))
)
val newData = flattenBirthday(oldData)
But in general dealing with nested immutable maps will be ugly. If you extract that data from JSONs (like in your example) it is better to use some library to deserialize that data into case class objects.

Scala: How to filter out maps with a common key value from a list of maps

I have a List of Map:
val input = List( Map("id" -> "abc", "val" -> 100),
Map("id" -> "abc", "val" -> 109),
Map("id" -> "bca", "val" -> 115),
Map("id" -> "acb", "val" -> 100),
Map("id" -> "cba", "val" -> 105),
Map("id" -> "cba", "val" -> 110),
Map("id" -> "cba", "val" -> 116) )
From which I need to filter out all the maps that share a common value for key "id". In other words, I have to only keep maps for which the value of key id is unique.
val output = List( Map("id" -> "bca", "val" -> 115),
Map("id" -> "acb", "val" -> 100) )
Use grouping
input.groupBy(x => x("id")).filter(y => y._2.size == 1).map(_._2)

How to use variable in reusable block in Play Framework 2 (Scala)

I can't find the way how to interpolate variable in reusable block.
I've tried this without luck:
#headers = #{
page match {
case "home" => Map(
"title" -> "Welcome",
"description" -> "Welcome to our site")
case "profile" => Map(
"title" -> "#user.name - #site.name",
"description" -> "Hello #user.name")
}
}
#headers = #{
page match {
case "home" => Map(
"title" -> "Welcome",
"description" -> "Welcome to our site")
case "profile" => Map(
"title" -> user.name + "-" site.name,
"description" -> "Hello" + user.name)
}
}
You should pass your variables as parameter:
#headers(user:User,site:Site) = #{
page match {
case "home" => Map(
"title" -> "Welcome",
"description" -> "Welcome to our site")
case "profile" => Map(
"title" -> user.name + "-" site.name,
"description" -> "Hello" + user.name)
}
}
Note: I guessed what type user and site had. You need to changes those of course.
Have a look at the documentation: https://www.playframework.com/documentation/2.4.x/ScalaTemplates#Declaring-reusable-blocks

nested pull with scala mongodb casbah

lets say i have a simple object
{
"id":"xyz"
"answers" : [{
"name" : "Yes",
}, {
"name" : "No",
}]
}
I want to remove answer Yes from the array
I'm trying something like this without much luck:
import com.mongodb.casbah.MongoCollection
val searchObject = MongoDBObject("id"->"xyz");
getCollection().update(searchObject,$pull( "answers" -> ( "name" -> "Yes")));
You need to declare ("name" -> "Yes") as a MongoDBObject because look at:
scala> $pull( "answers" -> ( "name" -> "Yes"))
res10: com.mongodb.casbah.query.Imports.DBObject = { "$pull" : { "answers" : [ "name" , "Yes"]}}
Which is not what you want, you want to pull a subdocument:
scala> $pull ( "answers" -> MongoDBObject("name" -> "Yes") )
res11: com.mongodb.casbah.query.Imports.DBObject = { "$pull" : { "answers" : { "name" : "Yes"}}}