simple question:
How can one use play-json (2.3.x) to sort all JsArrays in some JsValue (recursively)?
my usecase:
consider an app that uses Set[String] internally, and when data is requested,
the output JSON serialize the set as a JSON array. the order is not important.
now, if one wants to write some tests to cover this functionality, since the order of items is not important (it is a set after all. internally, and conceptually), and all I want to check is that everything returned as it should, I may want to compare the response JSON with an "expected" JSON object I create explicitly.
for that exact reason, I want to sort the JSON arrays, and compare the JsValue's.
how would one write such transformer?
EDIT:
I have managed to write a transformer that answers my needs, but it won't sort every JsArry in some JsValue. I'll post it here, since it might be useful for others, but it is not what I was asking for.
val jsonSortTransformer = (__ \ 'fields).json.update(
Reads.JsObjectReads.map{
case JsObject(xs) => JsObject(
xs.map{
case (n,jv) => {
n -> (jv match {
case JsArray(arr) if arr.forall(_.isInstanceOf[JsString]) => JsArray(arr.sortBy(_.as[String]))
case _ => jv
})
}
}
)
}
)
You can use the value property on JsArray to get a Seq[JsValue], then sort arbitrarily, and then recreate a JsArray. For example:
scala> myJsArray
play.api.libs.json.JsArray = ["11","4","5","1","22","2"]
scala> JsArray(myJsArray.value.sortBy(_.as[JsString].value.toInt))
play.api.libs.json.JsArray = ["1","2","4","5","11","22"]
If all you're doing is trying to compare actual and expected values of what you know is a set, you can also just use value on both properties, build a Set and check for equality:
Set(actual.value: _*) == Set(expected.value: _*)
Or sort them both:
val sortedSeq: JsArray => Seq[String] = array => array.value.map(_.toString).sorted
sortedSeq(actual) == sortedSeq(expected)
To recursively sort all the JsArrays in an arbitrary JsValue, it might look something like:
def sortArrays(json: JsValue): JsValue = json match {
case JsObject(obj) => JsObject(obj.toMap.mapValues(sortArrays(_)).toList)
case JsArray(arr) => JsArray(arr.map(sortArrays).sortBy(_.toString))
case other => other
}
scala> myObj
play.api.libs.json.JsValue = {"a":[2,1],"b":[{"c":[3,2]},{"d":[4,3]}],"e":{"f":[5,4]}}
scala> sortArrays(myObj)
play.api.libs.json.JsValue = {"a":[1,2],"b":[{"c":[2,3]},{"d":[3,4]}],"e":{"f":[4,5]}}
I'm afraid that #Ben's answer is quite incorrect.
I would approach this problem by defining an Ordering class for JsValues and then use its comparison method to verify equality (meaning this should actually be an object - not an anonymous class, as shown in the example).
One doesn't have to use Ordering, I just find it a bit more convenient than a simple compareTo method. Of course, one can also define this class/object as implicit.
val jsonOrdering: Ordering[JsValue] = new Ordering[JsValue]() {
override def compare(x: JsValue, y: JsValue): Int = {
x.getClass.getName.compareTo(y.getClass.getName) match {
case 0 =>
(x, y) match {
case (JsNull, JsNull) => 0
case (JsString(valueX), JsString(valueY)) =>
valueX.compareTo(valueY)
case (JsNumber(valueX), JsNumber(valueY)) =>
valueX.compare(valueY)
case (JsBoolean(boolX), JsBoolean(boolY)) =>
boolX.compareTo(boolY)
case (JsArray(elementsX), JsArray(elementsY)) =>
elementsX.size.compareTo(elementsY.size) match {
case 0 =>
elementsX
// .sorted(this) // uncomment if array order DOES NOT matter
.zip(elementsY
// .sorted(this) // uncomment if array order DOES NOT matter
)
.view
.map {
case (elementX, elementY) => compare(elementX, elementY)
}
.find(_ != 0)
.getOrElse(0)
case nonZero => nonZero
}
case (JsObject(fieldsX), JsObject(fieldsY)) =>
fieldsX.size.compareTo(fieldsY.size) match {
case 0 =>
fieldsX.toSeq
.sortBy(_._1)
.zip(fieldsY.toSeq.sortBy(_._1))
.view
.flatMap {
case ((keyX, valueX), (keyY, valueY)) =>
Seq(keyX.compareTo(keyY), compare(valueX, valueY))
}
.find(_ != 0)
.getOrElse(0)
case nonZero => nonZero
}
}
case nonZero => nonZero
}
}
I would perhaps split some parts into private/nested functions (I got lazy this time). Anyway, let's go over this:
Compare the two values' class names, and if they aren't the same then return the comparison between their names.
If the values are of any primitive JSON type, simply return the comparison between them.
If the values are arrays, then:
Compare their sizes, and if they aren't the same then return the comparison between the sizes.
Only if the order of arrays doesn't matter - sort each of the arrays (with the same ordering class; i.e, this is recursive).
Zip the elements of both arrays (so that you get an array of pairs of elements).
Find the first pair that its two elements are not the same, and return their comparison.
If no such pair exists, this means that the arrays are the same (return 0).
If the values are maps (objects):
Compare their sizes, and if they aren't the same then return the comparison between the sizes.
Turn the maps into a sequence of tuples, and sort these sequences by their key (first element of a tuple).
Zip the tuples of both sequences (so that you get an array of pairs of tuples).
Find the first pair that its tuples are not the same, and return their comparison. Compare these tuples in the following manner:
Compare their keys (strings), and return their comparison if they're not the same.
Compare their values (JsValue, thus using the same method recursively), and return their comparison if they're not the same.
Otherwise, they are the same.
If no such pair exists, this means that the maps (objects) are the same (return 0).
Note that although this ordering is consistent and deterministic, it is quite arbitrary and doesn't convey much logical meaning.
Related
I need to render a sorted map by a user-defined type.
SortedMap[X, Seq[Y]]
Json library should render the map as ordered by the X.name
case class X(order: Int, name: String) extends Ordered[X]
Assume I have X(1, "James"), X(2, "Mel"), X(3, "Ashley").
The output map should be
"James" : Seq(Y)
"Mel" : Seq(Y)
"Ashley": Seq(Y)
The inMap is correctly sorted (as viewed by the debugger), but after the rendering, the sorting order(X.order) is lost. Probably due to the toSeq. Any ideas?
implicit val myWrites = new Writes[SortedMap[X, Seq[Y]]] {
def writes(inMap: SortedMap[X, Seq[Y]]): JsValue =
Json.obj(inMap.map {case (s, o) =>
val r: (String, JsValueWrapper) = s.name() -> Json.toJson(o)
r
}.toSeq:_*)
}
So...
I never meet the word "render" used as "convert to"
ordering by key original key in SortedSet is lost after mapping because you change the key type so the result is ordered by a new key type (here: String)
if you want to preserve the order of items in between mapping I would suggest using ListMap
though in your particular case you can do away with Seq of tuples, as at the end of the day, this is what you need to produce
implicit val myWrites: Writes[SortedMap[X, Seq[Y]]] = new Writes[SortedMap[X, Seq[Y]]] {
def writes(inMap: SortedMap[X, Seq[Y]]): JsValue =
Json.obj(inMap.toSeq.map { case (s, o) =>
s.name() -> Json.toJson(o)
}:_*)
}
I have a sequence of case class A (id: UUID, profile: String, data: JsValue)
I'd like to sort the sequence by updated from the JsValue data field
data field looks like this
{
"doors":2,
"color":"Black",
"updated":"2019-09-24T15:59:21+0200",
"username":"John",
"year":2016
}
I tried
sequenceOfA.sortWith(_.data \ "updated" < _.data \ "updated") but that doesn't work because < is not a member of play's JsLookupResult
Casting it to String doesn't work either
sequenceOfA.sortWith((_.data \ "updated").as[String] < (_.data \ "updated").as[String])
What would be the most idiomatic way to do this in Scala?
What you would need is to handle absent field. This can be done via more safe approach with explicitly handling:
sequenceOfA.sortWith { case (left, right) =>
val leftUpdate = (left.data \ "update").validate[String].asOpt
val rightUpdate = (right.data \ "update").validate[String].asOpt
leftUpdate -> rightUpdate match {
case (Some(left), Some(right)) => left < right
case (None, Some(_)) => true // objects with `update` absent field goes first
case (Some(_), None) => false // objects with `update` present field goes after absent field
}
}
Or just invoke get method, which might throw an exception - which is highly unrecommended:
sequenceOfA.sortWith((m \ "updated").as[String].get < (_.data \ "updated").as[String].get)
Hope this helps!
A potential solution could be:
// Input is Seq[A] i.e val input: Seq[A]
// ConvertToTimestamp is a method which takes time as string and returns timestamp.
//This is required since time is in string and also timezones are involved.
//So this method will convert time string to UTC time(in epochs)
// def convertToTimeStamp(time: String): Long
val updated: Seq[Long] = A.map(value => (value.data \ "updated").as[String]).map(x => convertToTimestamp(x)) // fetch value
val pairs = input zip updated // List of ordered tuple
val sortedInput = pairs.sort(sortBy(pair => pair._2)).map(_._1).toSeq // sort using timestamp
The above solution assumes that updated field is not empty. If forsome input it is empty, change the function where we are parsing this field(while computing updated value as shown in above to have a default value based on the requirement(either 0 if you want such records to come first or Duration.Inf if you want those records to come last).
Let me know if it helps!!
I am starting up on Scala, doing a project with circe to handle JSON.
I am coming accross a lot of Either returns from functions, and I don't seem to find a elegant way to handle all of them.
For instance, for a single either, I do as in this snippet:
if (responseJson.isRight) {
//do something
} else {
//do something else
}
But what should I do when I have a lot of them in sequence, such as this example in which I just go straight for the right side and I feel I should be doing some extra validation:
ClassA(
someValue,
someValue,
someJson.hcursor.get[Double]("jsonKey1").right.get,
someJson.hcursor.get[Double]("jsonKey2").right.get,
someJson.hcursor.get[Double]("jsonKey3").right.get
)
How should/can I handle multiple Either objects (without ending up with a bunch of if-elses, or similar) when I want to get their contents if they are a Right, but not I am not sure they are always a Right ?
Lets say you have a case class,
case class Demo(i: Int, s: String)
and two eithers,
val intEither: Either[Throwable, Int] = ???
val stringEither: Either[Throwable, Int] = ???
So... lets start with the most basic and obvious one,
val demoEither: Either[Throwable, Demo] =
intEither.flatMap(i =>
stringEither.map(s => Demo(i, s))
)
Another way is to do the same as above is to use for-comprehensions,
val demoEither: Either[Throwable, Demo] =
for {
i <- intEither
s <- stringEither
} yield Demo(i, s)
But, monads are sequential, which means that if the first Either is a Left then you will not even look at the second Either and just get a Left. This is mostly undesirable for validations because you don't want to loose the validation information of all components, so what you actually want is an Applicative.
And Either is not an Applicative, you will have to use cats or scalaz or implement your own applicative for this.
cats provides the Validated applicative for this express purpose which lets you validate and keep all error information of the validated components.
import cats.data._
import cats.implicits._
val intValidated: ValidatedNec[Throwable, Int] =
intEither.toValidatedNec
val stringValidated: ValidatedNec[Throwable, String] =
stringEither.toValidatedNec
val demoValidated: ValidatedNec[Throwable, Demo] =
(intValidated, stringValidated).mapN(Demo)
val demoEither: Either[List[Throwable], Demo] =
demoValidated.leftMap(errorNec => errorNec.toList)
Or, if you are doing this just once and don't want to depend on cats, you can just use pattern-matching which is very versatile
val demoEither: Either[List[Throwable], Demo] =
(intEither, stringEither) match {
case (Right(i), Right(s)) => Right(Demo(i, s))
case (Left(ti), Left(ts)) => Left(List(ti, ts))
case (Left(ti), _) => Left(List(ti))
case (_, Left(ts)) => Left(List(ts))
}
How should/can I handle multiple Either objects (without ending up with a bunch of if-elses, or similar) when I want to get their contents if they are a Right, but not I am not sure they are always a Right?
So you have some Either instances, all with the same type signature.
val ea :Either[Throwable,String] = Right("good")
val eb :Either[Throwable,String] = Left(new Error("bad"))
val ec :Either[Throwable,String] = Right("enough")
And you want all the Right values, ignoring any Left values.
List(ea, eb, ec).collect{case Right(x) => x}
//res0: List[String] = List(good, enough)
You don't know which Either contains which String but I think that's what you asked for.
How do I implement a 4:1 Mux in chisel without using 2:1 Muxes? Is there a way where we can select one of the inputs of the N inputs by having something like Mux(sel, A,B,C,D.......N) where N can be taken in as a parameter? I am aware of the MuxCase in chisel but I am yet to find an example that makes use of MuxCase, any sort of documentation or example regarding this is greatly appreciated. Thank You.
There are a couple of usages in rocket-chip in MultiWidthFifo.scala
It's pretty straightforward. It takes a default value for what happens if none of the supplied conditions is true, otherwise it looks through a sequence of tuples, where each tuple of the form (bool condition, result) often written condition -> result. The return value is the result from the first boolean condition that is true.
Here is a toy example of a module that passes the number of input bools into a module which then uses that value to construct a sequence of mux cases.
class UsesMuxCase(numCases: Int) extends Module {
val io = IO(new Bundle {
val output = Output(UInt(10.W))
val inputs = Input(Vec(numCases, Bool()))
})
val cases = io.inputs.zipWithIndex.map { case (bool, index) =>
bool -> index.U(10.W)
}
io.output := MuxCase(0.U(10.W), cases)
}
I'm trying to save the parameters used to sort a sequence in Scala for deferred execution at a later time.
For example, instead of "list.sortBy (.value)", I want to save the (".value") sort function, and retrieve this sort function ("_.value") at a later time for the actual sorting.
How do I save and retrieve the sort function arguments for deferred execution? Here is some sample test code:
class SortTest {
def testSort () = {
val myClass = new MyClass(0)
val list = List (myClass, new MyClass(1), new MyClass(2), new MyClass(3), new MyClass(4))
// Want to sort by value attribute, but don't want to sort right away. Rather
// how do I save the sort function, and retrieve it at a later time for execution?
list.sortBy(_.value)
// save the sort function (i.e. sort by the value attribute of myClass)
// something similar to the following syntax
myClass.setSortFunction (_.value)
// retrieve the sort function and sort the list
list.sortBy(myClass.getSortFunction())
}
class MyClass (d:Int){
val value = d
val sortFunc = null
// what should be the signature of this function ?
def setSortFunction (sortFunc: ()) = {
this.sortFunc = sortFunc
}
// what should be the return type of this function?
def getSortFunction () = {
return sortFunc
}
}
}
You could do something like this:
val sortFunction = (x : { def value: Int } ) => x.value
At this point, you might not be happy with the hardcoding of Int. Unfortunately, a function must have well defined types, so I cannot make this generic on the return type.
One could instead make it a definition:
def sortFunction[T] = (x : { def value: T } ) => x.value
However, you cannot pass definitions around, only values, and values cannot be parameterized.
On the other hand, you are approaching this the wrong way -- there's an assumption there that sortBy takes a function as a parameter, and only that. Not true: sortBy takes two parameters: a function, and an Ordering. If you don't save the ordering, you cannot sort it.
And here we get to the other problem... the function must have a type MyClass => T, and the ordering must be of type Ordering[T]. Without knowing in advance what T is, you cannot save that.
Fortunately, and the reason why Ordering is a good idea, you can simply create an Ordering[MyClass], and use that!
Here's how:
class MyClass(d: Int) {
val value = d
private var sortFunction: Ordering[MyClass] = _
def setSortFunction[T : Ordering](f: MyClass => T) {
sortFunction = Ordering by f
}
def getSortFunction = sortFunction
}
And you use it like this:
list.sorted(myClass.getSortFunction)
Notice that instead of sortBy it uses sorted. The method sortBy is implemented by creating an Ordering and calling sorted with it, so you are not losing any performance.