Parsing nested collection in scala - json

I have a collection structure as shown below. I need to build the statement based on k,v pairs in the map. The level of nesting is not known prior and code needs to parse it dynamically.
Structure:
case class Step(id: Option[String], stepId: Option[String], parameters: Option[Map[String, Any]])
val sample = Step(id = Some("a1"), Some("case"), Some(
Map(
"CONDITIONAL" -> List(
Map("comparison" -> List(
Map("comparision" -> List(
Map("comparision" -> List(
Map("comparator" -> List(
Map(
"EVAL" -> "c11",
"OPERATION" -> "equals to",
"WHEN" -> List("A")),
Map(
"eval" -> "c12",
"operation" -> "in",
"when" -> List("B")),
Map(
"eval" -> "c13",
"operation" -> "starts with",
"when" -> List("C")
)),
"operator" -> "OR"
),
Map("comparator" -> List(
Map(
"EVAL" -> "c21",
"OPERATION" -> "equals to",
"WHEN" -> List("A")),
Map(
"eval" -> "c22",
"operation" -> "in",
"when" -> List("B")
)),
"operator" -> "OR"
)),
"operator" -> "AND"
),
Map("comparator" -> List(
Map(
"EVAL" -> "c31",
"OPERATION" -> "equals to",
"WHEN" -> List("A")),
Map(
"EVAL" -> "c32",
"OPERATION" -> "in",
"WHEN" -> List("B")),
Map(
"EVAL" -> "c33",
"OPERATION" -> "starts with",
"WHEN" -> List("C")
)),
"operator" -> "OR"
)),
"operator" -> "OR"
)),
"THEN" -> "result"
)),
"ELSE" -> "default"
))
)
val stepsLower = sample.parameters.get.map(p => p._1.toLowerCase -> p._2)
val comparisons = stepsLower("conditional").asInstanceOf[List[Map[String, Any]]]
Now I need to parse data under the conditional (in comparisons) by evaluating the structure.
The result should be ((c11 OR c12 OR c13 ) AND (c21 OR c22)) OR (c31 OR c32 OR c33 OR c34)

Here is the code snippet that should work for all your use cases.
case class Step(id: Option[String], stepId: Option[String], parameters: Option[Map[String, Any]])
val sample1 = Step(id = Some("a1"), Some("case"), Some(Map(
"CONDITIONAL" -> List(
Map("comparison" -> List(
Map("comparison" -> List(
Map("comparison" -> List(
Map("comparator" -> List(
Map(
"eval" -> "c11",
"operation" -> "equals to",
"when" -> List("A")),
Map(
"eval" -> "c12",
"operation" -> "in",
"when" -> List("B")),
Map(
"eval" -> "c13",
"operation" -> "starts with",
"when" -> List("C")
)),
"operator" -> "OR"
),
Map("comparator" -> List(
Map(
"eval" -> "c21",
"operation" -> "equals to",
"when" -> List("A")),
Map(
"eval" -> "c22",
"operation" -> "in",
"when" -> List("B")
)),
"operator" -> "OR"
)),
"operator" -> "AND"
),
Map("comparator" -> List(
Map(
"eval" -> "c31",
"operation" -> "equals to",
"when" -> List("A")),
Map(
"eval" -> "c32",
"operation" -> "in",
"when" -> List("B")),
Map(
"eval" -> "c33",
"operation" -> "starts with",
"when" -> List("C")
)),
"operator" -> "OR"
)),
"operator" -> "OR"
)),
"then" -> "result"
)),
"else" -> "default"
))
)
val sample2 = Step(id = Some("a1"), Some("case"), Some(
Map(
"conditional" -> List(
Map("comparison" -> List(
Map("comparison" -> List(
Map("comparator" -> List(
Map(
"eval" -> "c11",
"operation" -> "equals to",
"when" -> List("A")),
Map(
"eval" -> "c12",
"operation" -> "in",
"when" -> List("B", "b"))),
"operator" -> "AND"),
Map("comparator" -> List(
Map("eval" -> "c21",
"operation" -> "equals to",
"when" -> List("A")),
Map("eval" -> "c31",
"operation" -> "equals to",
"when" -> List("A"))),
"operator" -> "OR")),
"operator" -> "OR")),
"then" -> "something"
),
Map("comparison" -> List(
Map("comparison" -> List(
Map("comparator" -> List(
Map(
"eval" -> "c111",
"operation" -> "equals to",
"when" -> List("A")),
Map(
"eval" -> "c121",
"operation" -> "in",
"when" -> List("B", "b"))
),
"operator" -> "AND"),
Map("comparator" -> List(
Map(
"eval" -> "c212",
"operation" -> "equals to",
"when" -> List("A")),
Map(
"eval" -> "c313",
"operation" -> "equals to",
"when" -> List("A"))
),
"operator" -> "OR")),
"operator" -> "AND")),
"then" -> "somethingelse")
),
"else" -> "default"
)
))
def constructCase(c: List[Map[String, Any]], parentOp: Option[String] = None, parentThen: Option[String] = None): String = {
val result = c.map { x =>
val currentOp: Option[String] = x.get("operator").asInstanceOf[Option[String]]
val currentThen: Option[String] = x.get("then").asInstanceOf[Option[String]]
if (x contains "comparator") {
"(" + x("comparator").asInstanceOf[List[Map[String, Any]]].map(x => s"${x("eval")} ${x("operation")} ${x("when").asInstanceOf[List[String]].mkString(",")}").mkString(" " + currentOp.getOrElse("") + " ") + ")"
} else {
val tmp = constructCase(x("comparison").asInstanceOf[List[Map[String, Any]]], currentOp, currentThen)
if (tmp contains "WHEN") tmp else "(" + tmp + ")"
}
}.mkString(if (parentOp.isDefined) s" ${parentOp.get} " else "")
if (parentThen.isDefined) s"WHEN $result THEN ${parentThen.get}\n" else result.trim
}
val samples = Set(sample1, sample2)
samples.foreach { sample =>
val stepsLower = sample.parameters.get.map(p => p._1.toLowerCase -> p._2)
val comparisons = stepsLower("conditional").asInstanceOf[List[Map[String, Any]]]
println("CASE \n" + constructCase(comparisons) + s"\nELSE ${stepsLower("else")}")
println("")
}
Here is the result
CASE
WHEN (((c11 equals to A OR c12 in B OR c13 starts with C) AND (c21 equals to A OR c22 in B)) OR (c31 equals to A OR c32 in B OR c33 starts with C)) THEN result
ELSE default
CASE
WHEN ((c11 equals to A AND c12 in B,b) OR (c21 equals to A OR c31 equals to A)) THEN something
WHEN ((c111 equals to A AND c121 in B,b) AND (c212 equals to A OR c313 equals to A)) THEN somethingelse
ELSE default
Let me know if it doesn't work for any of your use case.

Related

Prove transformation between data types correctness

I have two data types and a transformation function to convert from one to another
data TTerm : Set where
TVar : Nat -> TTerm
TPrim : TPrim' -> TTerm
TDef : QName -> TTerm
TCon : QName -> TTerm
TLam : TTerm -> TTerm
TApp : TTerm -> List TTerm -> TTerm
TLet : TTerm -> TTerm -> TTerm
TLit : Literal -> TTerm
TErased : TTerm
TError : TError' -> TTerm
data GoTerm : Set where
Self : GoTerm
Local : LocalId -> GoTerm
Global : GlobalId -> GoTerm
GoVar : Nat -> GoTerm
GoSwitch : GoTerm -> List GoTerm -> GoTerm
GoCase : MemberId -> Nat -> Nat -> Nat -> List GoTerm -> GoTerm
GoMethodCall : MemberId -> List GoTerm -> GoTerm
GoMethodCallParam : GoTerm -> TypeId -> GoTerm
GoCreateStruct : MemberId -> List GoTerm -> GoTerm
GoIf : GoTerm -> GoTerm -> GoTerm -> GoTerm
GoLet : String -> GoTerm -> GoTerm -> GoTerm
PrimOp : GoTerm -> GoTerm -> GoTerm -> GoTerm
ReturnExpression : GoTerm -> TypeId -> GoTerm
GoInt : Nat -> GoTerm
Const : String -> GoTerm
UndefinedTerm : GoTerm
GoErased : GoTerm
Null : GoTerm
I have also created additional function that maps back from GoTerm to TTerm, so now I have two functions that do opposite things and would like to prove that a ≡ (decompileTerm (compileTerm a)). But so far have not found a way to do that, maybe someone could put me on the right tracks here?

How to read Map values from foreach in scala

// Reads Json file
val input_file = ("\\path\\to\\MyNew.json");
val json_content = scala.io.Source.fromFile(input_file).mkString
// parsing the json file
val details = JSON.parseFull(json_content)
// checking the matched result
details match {
case mayBeList: Some[Map[String, Any]] =>
val z = mayBeList.get.tails.toSet.flatten
z.foreach(println)
case None => println("Parsing failed")
case other => println("Unknown data structure: " + other)
}
getting following Output:
Map(Name -> Harish, Company -> In Equity, Sal -> 50000)
Map(Name -> Veer, Company -> InOut, Sal -> 20000)
Map(Name -> Zara, Company -> InWhich, Sal -> 90000)
Map(Name -> Singh, Company -> InWay, Sal -> 30000)
Map(Name -> Chandra, Company -> InSome, Sal -> 60000)
Expected Output
Harish, In Quality, 50000- (only values of Map)
Use .values for the values and .keys for the keys.
val m: Map[String, Int] = Map("a" -> 1, "b" -> 2)
m.values // res0: Iterable[Int] = MapLike(1, 2)
m.keys // res1: Iterable[String] = Set(a, b)
All you need is to iterate though the elements of your list i.e. z and extract values from each map like this,
List(Map("Name" -> "Harish", "Company" -> "In Equity", "Sal" -> 50000),
Map("Name" -> "Veer", "Company" -> "InOut", "Sal" -> 20000),
Map("Name" -> "Zara", "Company" -> "InWhich", "Sal" -> 90000),
Map("Name" -> "Singh", "Company" -> "InWay", "Sal" -> 30000),
Map("Name" -> "Chandra", "Company" -> "InSome", "Sal" -> 60000)
)
.map(_.values.toList).foreach(println)
//List[List[Any]] = List(List(Harish, In Equity, 50000), List(Veer, InOut, 20000), List(Zara, InWhich, 90000), List(Singh, InWay, 30000), List(Chandra, InSome, 60000))
Hope this helps you.
Update
In response to your comment, use this code
import scala.util.parsing.json._
val input_file = ("C:\\Users\\VishalK\\IdeaProjects\\ScalaCassan\\src\\main\\scala\\MyNew.json");
val json_content = scala.io.Source.fromFile(input_file)
// parsing the json file
val details: Option[Any] = JSON.parseFull(json_content.mkString)
details match {
case mayBeList: Some[Any] =>
mayBeList.getOrElse(Seq.empty[Map[String, Any]]).asInstanceOf[List[Map[String, Any]]].map(_.values.toList).toSet
case None => println("Parsing failed")
}
in your match block :
In first case I don't get why you are using .tails.toSet.flatten on Any data type.
You can remove the third case as Some and None are the only possible outcomes of Option data-type.
scala> val l = List(Map("Name" -> "Harish", "Company" -> "In Equity", "Sal" -> 50000),
| Map("Name" -> "Veer", "Company" -> "InOut", "Sal" -> 20000),
| Map("Name" -> "Zara", "Company" -> "InWhich", "Sal" -> 90000),
| Map("Name" -> "Singh", "Company" -> "InWay", "Sal" -> 30000),
| Map("Name" -> "Chandra", "Company" -> "InSome", "Sal" -> 60000)
| )
l: List[scala.collection.immutable.Map[String,Any]] = List(Map(Name -> Harish, Company -> In Equity, Sal -> 50000), Map(Name -> Veer, Company -> InOut, Sal -> 20000), Map(Name -> Zara, Company -> InWhich, Sal -> 90000), Map(Name -> Singh, Company -> InWay, Sal -> 30000), Map(Name -> Chandra, Company -> InSome, Sal -> 60000))
scala> l.map(_.values).foreach(x => println(x.toList.mkString(", ")))
Harish, In Equity, 50000
Veer, InOut, 20000
Zara, InWhich, 90000
Singh, InWay, 30000
Chandra, InSome, 60000

How to convert a Map to a Json in play scala

How can I convert the following Map structure which is a Map[String,Any] to a Json in scala? I am using Play.
val result = s
.groupBy(_.dashboardId)
.map(
each => Map(
"dashboardId" -> each._1,
"cubeId" -> each._2.head.cubeid,
"dashboardName" -> each._2.head.dashboardName,
"reports" -> each._2.groupBy(_.reportId).map(
reportEach => Map(
"reportId" -> reportEach._1,
"reportName" -> reportEach._2.head.reportName,
"selectedColumns" -> reportEach._2.groupBy(_.selectedColumnid).map(
selectedColumnsEach => Map(
"selectedColumnId" -> selectedColumnsEach._1,
"columnName" ->
selectedColumnsEach._2.head.selectColumnName.orNull,
"seq" ->selectedColumnsEach._2.head.selectedColumnSeq,
"formatting" -> selectedColumnsEach._2
)
)
)
)
)
)
You cannot convert a Map[String, Any] to Json but you can convert a Map[String, String] or Map[String, JsValue].
In your case, you can do by converting each map value to a JsValue before hand with:
Map(
"dashboardId" -> Json.toJson(each._1),
"cubeId" -> Json.toJson(each._2.head.cubeid),
"dashboardName" -> Json.toJson(each._2.head.dashboardName),
"reports" -> Json.toJson(each._2.groupBy(_.reportId).map(
reportEach => Map(
"reportId" -> Json.toJson(reportEach._1),
"reportName" -> (reportEach._2.find(_.reportName != null) match {
case Some(reportNameData) => Json.toJson(reportNameData.reportName)
case None => JsNull
})),
...
)
I read the results into a Seq[Map[String,Any]] by using .toSeq and then used toJson to convert it into a Json this worked.
val s = new SaveTemplate getReportsWithDashboardId(dashboardId)
val result : Seq[Map[String,Any]] = s.groupBy(_.dashboardId)
.map(
each => Map(
"dashboardId" -> each._1,
"cubeId" -> each._2.head.cubeid,
"dashboardName" -> each._2.head.dashboardName,
"reports" -> each._2.groupBy(_.reportId).map(
reportEach => Map(
"reportId" -> reportEach._1,
"reportName" -> (reportEach._2.find(_.reportName != null) match {
case Some(reportNameData) => reportNameData.reportName
case None => null
}),
"selectedColumns" -> reportEach._2.groupBy(_.selectedColumnid).map(
selectedColumnsEach => Map(
"selectedColumnId" -> selectedColumnsEach._1,
"columnName" -> selectedColumnsEach._2.head.selectColumnName.orNull,
"seq" ->selectedColumnsEach._2.head.selectedColumnSeq,
"formatting" -> Map(
"formatId" -> (selectedColumnsEach._2.find(_.formatId != null) match {
case Some(reportNameData) => reportNameData.formatId
case None => null
}),
"formattingId" -> (selectedColumnsEach._2.find(_.formattingid != null)
match {
case Some(reportNameData) => reportNameData.formattingid
case None => null
}),
"type" -> (selectedColumnsEach._2.find(_.formattingType != null) match
{
case Some(reportNameData) => reportNameData.formattingType
case None => null
})
)
)
)
)
)
)
).toSeq
val k = toJson(result)
Ok(k)

How to get first element from Any

My Json is a list of objects. I want to get the first one, but Any is making it difficult:
scala> import scala.util.parsing.json._
import scala.util.parsing.json._
scala> val str ="""
| [
| {
| "UserName": "user1",
| "Tags": "one, two, three"
| },
| {
| "UserName": "user2",
| "Tags": "one, two, three"
| }
| ]""".stripMargin
str: String =
"
[
{
"UserName": "user1",
"Tags": "one, two, three"
},
{
"UserName": "user2",
"Tags": "one, two, three"
}
]"
scala> val parsed = JSON.parseFull(str)
parsed: Option[Any] = Some(List(Map(UserName -> user1, Tags -> one, two, three), Map(UserName -> user2, Tags -> one, two, three)))
scala> parsed.getOrElse(0)
res0: Any = List(Map(UserName -> user1, Tags -> one, two, three), Map(UserName -> user2, Tags -> one, two, three))
scala> parsed.getOrElse(0)(0)
<console>:13: error: Any does not take parameters
parsed.getOrElse(0)(0)
How do I get the first element?
You need to pattern match the result(Option[Any]) to List[Map[String, String]].
1) Patter match example,
scala> val parsed = JSON.parseFull("""[{"UserName":"user1","Tags":"one, two, three"},{"UserName":"user2","Tags":"one, two, three"}]""")
scala> parsed.map(users => users match { case usersList : List[Map[String, String]] => usersList(0) case _ => Option.empty })
res8: Option[Equals] = Some(Map(UserName -> user1, Tags -> one, two, three))
better pattern match,
scala> parsed.map(_ match { case head :: tail => head case _ => Option.empty })
res13: Option[Any] = Some(Map(UserName -> user1, Tags -> one, two, three))
2) Or else you can cast the result (Option[Any]) but not recommended(as cast might throw ClassCastException),
scala> parsed.map(_.asInstanceOf[List[Map[String, String]]](0))
res10: Option[Map[String,String]] = Some(Map(UserName -> user1, Tags -> one, two, three))

What is the Scala way of summing fields of list of tuples?

I want to reduce list of tuples that have a common field such as x by summing y fields.
Input:
List(("x" -> "foo", "y" -> 1000),
("x" -> "foo", "y" -> 1),
("x" -> "bar", "y" -> 101))
Output:
List(("x" -> "foo", "y" -> 1001),
("x" -> "bar", "y" -> 101))
What is the good approach for that? foldLeft or reduce?
Your item should be tuple:
List(("x" -> "foo", "y" -> 1000),("x" -> "foo", "y" -> 1), ("x" -> "bar", "y" -> 101))
use groupBy to group your key, and mapValues to generate the target:
List(("x" -> "foo", "y" -> 1000),("x" -> "foo", "y" -> 1), ("x" -> "bar", "y" -> 101))
.groupBy(x => x._1)
.mapValues(t => t.head._2._1 -> t.foldLeft(0.0)(_ + _._2._2))
The Output:
scala.collection.immutable.Map[(String, String),(String, Double)] =
Map((x,foo) -> (y,1001.0), (x,bar) -> (y,101.0))