In the example given on the json4s readme https://github.com/json4s/json4s#serializing-non-supported-types the match only works if the fields are order {"start":0,"end":0}. If the start and end fields are swapped then the match doesn't work. Is there anyway to write the below case match such that the JSON field ordering doesn't matter?
case JObject(JField("start", JInt(s)) :: JField("end", JInt(e)) :: Nil)
I haven't used this library, so I am not sure if this is the right approach: (I came up with this after spending a couple of minutes looking at the docs)
class IntervalSerializer extends CustomSerializer[Interval](format => (
{
case x: JObject =>
x.obj.sortBy { case (k,_) => k } match {
case JField("end", JInt(e)) :: JField("start", JInt(s)) :: Nil =>
new Interval(start = s.longValue(), end = e.longValue())
}
},
{
case x: Interval =>
JObject(JField("start", JInt(BigInt(x.startTime))) ::
JField("end", JInt(BigInt(x.endTime))) :: Nil)
}
))
The idea is to sort the fields alphabetically, and then create the Interval class.
I had a different but related issue that made me discover the the "extract" function in json4s. It solves the ordering issue.
case x: JObject =>
Interval((x \ "start").extract[Int],(x \ "end").extract[Int])
If you need a more involved example, you can check this github ticket.
Related
Say I have the following:
trait PropType
case class PropTypeA(String value) extends PropType
case class PropTypeB(String value) extends PropType
case class Item(
propTypeA: PropTypeA,
propTypeB: PropTypeB
)
and that I'm given a List[PropType]. How would I go with combining this into a List[Item]?
That is (and assuming we only have PropTypeA(name: String) and PropTypeB(name: String) to make this shorter / easier to follow hopefully) given this:
List[PropType](
PropTypeA("item1-propTypeA"),
PropTypeB("item1-propTypeB"),
PropTypeA("item2-propTypeA"),
PropTypeB("item2-propTypeB")
]
I'd like to get the equivalent of:
List[Item](
Item(PropTypeA("item1-propTypeA"), PropTypeB("item1-propTypeB")),
Item(PropTypeA("item2-propTypeA"), PropTypeB("item2-propTypeB"))
)
Kind of table building from linearized rows across columns, if that makes sense.
Note that in general there might be incomplete "rows", e.g. this:
List[PropType](
PropTypeA("item1-propTypeA"),
PropTypeB("item1-propTypeB"),
PropTypeB("itemPartialXXX-propTypeB"),
PropTypeA("itemPartialYYY-propTypeA"),
PropTypeA("item2-propTypeA"),
PropTypeB("item2-propTypeB")
]
should generate the same output as the above, with the logic being that PropTypeA always marks the start of a new row and thus everything "unused" is discarded.
How should I approach this?
Something like this will work with examples you mentioned.
list.grouped(2).collect { case Seq(a: PropTypeA, b: PropTypeB) => Item(a,b) }.toList
However it is unclear from your question what other cases you want to handle and how. For example, how exactly do you define the "partial" occurrence. Are there always two elements in reverse order? Can there be just one, or three? Can there be two As in a row? Or three? Or two Bs?
For example, A, A, A, A, B or B, B, A, A, B or just A?
Depending on how you answer those question, you'll need to somehow "pre-filter" the list before hand.
Here is an implementation based on the last phrase in your question: "PropTypeA always marks the start of a new row and thus everything "unused" is discarded." It only looks for instances where an A is immediately followed by B and discards everything else:
list.foldLeft(List.empty[PropType]) {
case ((a: PropTypeA) :: tail, b: PropTypeB) => b :: a :: tail
case ((b: PropTypeB) :: tail, a: PropTypeA) => a :: b :: tail
case (Nil, a: PropTypeA) => a :: Nil
case (_ :: tail, a: PropTypeA) => a :: tail
case (list, _) => list
}.reverse.grouped(2).collect {
case Seq(a: PropTypeA, b: PropTypeB) => Item(a,b)
}.toList
If you have more than just two types, then there are even more questions: what happens if stuff after A comes in wrong order for example? Like what do you do with A,B,C,A,C,B?
But basically, it would be the same idea as above: if next element is of the type you expect in the the sequence, add it to the result, otherwise discard sequence and keep going.
we can use the tail recursion function to generate the list of a new type.
def transformType(proptypes: List[PropType]): List[Item] =
{
// tail recursion function defined
#tailrec
def transform(proptypes: List[PropType], items: List[Item]): List[Item]=
{
proptypes match {
case (first:PropTypeA) :: (second:PropTypeB) :: tail=> transform(tail, items :+ Item(first, second))
case (first:PropTypeA) :: (second:PropTypeA) :: tail => transform(second :: tail, items :+ Item(first, PropTypeB("")))
case (first:PropTypeB) :: tail => transform(tail, items :+ Item(PropTypeA(""), first))
case (first:PropTypeA) :: tail => transform(tail, items :+ Item(first, PropTypeB("")))
case _ => items
}
}
transform(proptypes, List.empty[Item])
}
you can find the working link here
I just started working with scala and am trying to get used to the language. I was wondering if the following is possible:
I have a list of Instruction objects that I am looping over with the foreach method. Am I able to add elements to my Instruction list while I am looping over it? Here is a code example to explain what I want:
instructions.zipWithIndex.foreach { case (value, index) =>
value match {
case WhileStmt() => {
---> Here I want to add elements to the instructions list.
}
case IfStmt() => {
...
}
_ => {
...
}
Idiomatic way would be something like this for rather complex iteration and replacement logic:
#tailrec
def idiomaticWay(list: List[Instruction],
acc: List[Instruction] = List.empty): List[Instruction] =
list match {
case WhileStmt() :: tail =>
// add element to head of acc
idiomaticWay(tail, CherryOnTop :: acc)
case IfStmt() :: tail =>
// add nothing here
idiomaticWay(tail, list.head :: acc)
case Nil => acc
}
val updatedList = idiomaticWay(List(WhileStmt(), IfStmt()))
println(updatedList) // List(IfStmt(), CherryOnTop)
This solution works with immutable list, returns immutable list which has different values in it according to your logic.
If you want to ultimately hack around (add, remove, etc) you could use Java ListIterator class that would allow you to do all operations mentioned above:
def hackWay(list: util.List[Instruction]): Unit = {
val iterator = list.listIterator()
while(iterator.hasNext) {
iterator.next() match {
case WhileStmt() =>
iterator.set(CherryOnTop)
case IfStmt() => // do nothing here
}
}
}
import collection.JavaConverters._
val instructions = new util.ArrayList[Instruction](List(WhileStmt(), IfStmt()).asJava)
hackWay(instructions)
println(instructions.asScala) // Buffer(CherryOnTop, IfStmt())
However in the second case you do not need scala :( So my advise would be to stick to immutable data structures in scala.
Using Scala, is there any way to dynamically construct a list patterns to be pattern matched against?
For example, suppose I'm using stable identifiers to parse a list of Strings, like this:
def matchingAndDispatch(xs: List[String])= {
case `namespace` :: value :: `elementTerminator` :: rest => {
// Do Something...
}
case `openBracket` :: rest => {
// Do Something Else...
}
case `closeBracket` :: `elementTerminator` :: rest => {
// Or perhaps something else...
}
}
Now, suppose there are going to be a lot of case clauses and I wanted the ability to store them in a collection of some sort that could be changed at runtime - not necessarily the patterns themselves, but the collection of patterns could be changed. I've made up the imaginary class MatchClause in the code below to explain more or less what I have in mind - basically traverse a collection of pattern(i.e. Match Clauses) and match one at a time:
def matchingAndDispatch(xs: List[String], matchingClauses:List[MatchClause])= {
if(!matchingClauses.empty){
case matchingClauses.head => {
// Do Something...
}
case _ => matchingAndDispatch(xs, matchingClause.tail)
}
}else throw new Error("no match")
Is there anything in the Scala API that would serve this purpose? I haven't found anything. Or perhaps I'm going about this the wrong way?
val `namespace` = "namespace"
val `elementTerminator` = "elementTerminator"
val `openBracket` = "openBracket"
val `closeBracket` = "closeBracket"
// list of partial functions from list of strings to string:
val patterns = List[PartialFunction[List[String], String]](
{ case `namespace` :: value :: `elementTerminator` :: rest => "case1" },
{ case `openBracket` :: rest => "case2" },
{ case `closeBracket` :: `elementTerminator` :: rest => "case3" })
def matchingAndDispatch(xs: List[String], patterns: List[PartialFunction[List[String], String]]): String = {
patterns.find(_.isDefinedAt(xs)).map(_(xs)).getOrElse("unknown")
}
Test:
matchingAndDispatch(List("namespace", "somevalue", "elementTerminator"), patterns)
> case1
matchingAndDispatch(List("namespace", "somevalue", "elementTerminator", "more"), patterns)
> case1
matchingAndDispatch(List("namespace", "somevalue", "not_terminator", "more"), patterns)
> unknown
You can declare a stable identifier within a local scope. That is, you can write
val hd = matchingClauses.head
xs match {
case `hd` => ???
}
How to build an url with two or many params?
i've a case class:
case class PageDetail(index: String, id: String)
i'm trying to do a menu val:
val menu = Menu.params[(String,String)]( "pageDetail", "Page Detail",
ids => { case Full(index) :: Full(id) :: Nil => Full((index, id))},
pi => { case (index, id) => index :: id :: Nil }) / "admin" / "detail"
i would like to obtain a link as .../admin/detail/indexxxxxxx/idddddddddd where indexxxxxxx and idddddddddd are my params.
as is doesn't work. Error in compile time. How can i do?
Thanks
Most likely, the issue is in your extractor pattern. When you are matching on your list here:
case Full(index) :: Full(id) :: Nil => Full((index, id))
The parameters are always going to be defined, so the Full is not possible. You can use functions, such as AsInt to require the parameter to be an Int, or else it will look for a String. You'd most likely want to start with the following (Or some variation on that):
case index :: id :: Nil => Full((index, id))
If you are using Empty to mean the parameter is optional, then you would simply add a second case statement after it with the parameter omitted.
Also, you probably need to add / ** to the end of you / "admin" / "detail" mapping so it knows to grab the parameters from there.
So, the code should look something like this:
val menu = Menu.params[(String,String)]( "pageDetail", "Page Detail",
{
case index :: id :: Nil => Full((index, id))
}, {
case (index, id) => index :: id :: Nil
}
) / "admin" / "detail" / **
I'd like to be able to use a single variable multiple times within one pattern, so that it will only match if the same value is present in all places, such as
list match {
case x :: x :: xs => // recurse
}
which would match List(1,1,2) but would not match List(1,2,1). But this does not compile with error: x is already defined as value x.
In researching this question, I found out that I can also include a guard in the case clause, so I can do
list match {
case x1 :: x2 :: xs if x1==x2 => // recurse
}
which seems to work the same way (it does, right?). This is good, but it wouldn't look as clean if I wanted the same value in many places, like
list match {
case x1::x2::x3::x4::xs if x1==x2 && x2==x3 && x3==x4 => // recurse
}
Is there any more elegant way I can do this?
A few notes: Yes, I am just learning scala, if that wasn't clear, so I'm not sure this is something I'd ever really want to do, but I'm just interested in what's possible. In that regard, I'm not really looking for a completely different solution, like takeWhile or filter or something, but more so am specifically interested in pattern matching.
Scala doesn't provide quite that much flexibility with its matches (which may be a good thing, as one has to be aware of errors arising from unintentional variable re-use).
If you have a large number of identical items, you might want to consider a nested match (but note that you won't fail out of the inner match to be completed later down the outer match, so you have to handle everything locally):
list match {
case x :: rest => rest match {
case `x` :: `x` :: `x` :: xs => println("Four of the same")
case _ => println("Well, nonempty at least")
}
case _ => println("Boring, there's nothing here!")
}
Note the backticks which mean "we've already got this variable, check against it, don't set it!".
Alternatively, if you have specialized functionality that you use repeatedly, you can create a custom matcher:
object FourOf {
def unapplySeq(xs: List[Int]): Option[(Int, List[Int])] = xs match {
case x :: y :: z :: a :: rest if x==y && y==z && z==a => Some((x,rest))
case _ => None
}
}
and then use it whenever you need that complicated pattern:
list match {
case FourOf(x,rest) => println("four of the same")
case x :: more => println("Nonempty")
case _ => println("Yawn")
}
Neither of these are quite as tidy and flexible as what you were apparently hoping for, but then again, I'm not sure flipping between assigning and testing the same variable in a match statement is a good way to write clear code anyway.
For many repeats you might use stable identifiers to do a comparison (instead of catching a value):
val x = list.head
list match {
case `x`::`x`::`x`::`x`::xs => ....
}
But note that this won't work on empty list (you just cannot get head of it).
I think Rex's answer rocks. I am a fan of unapplySeq. But here's a not-so-clever-and-maybe-wasteful alternative, if your main bother is just with the sequence of =='s in each guard.
So in the TMTOWTDI spirit:
def same[A](xs: A*) = xs forall (xs.head==)
// Then in your pattern match,
list match {
// case x1::x2::x3::x4::xs if x1==x2 && x2==x3 && x3==x4 => // recurse
case x1::x2::x3::x4::xs if same(x1,x2,x3,x4) => // recurse
}
I like Om's answer as well, so here's an adaptation:
list.headOption map (x => list match {
case `x`::`x`::`x`::`x`::xs => //...;
case _ => // ...
}) getOrElse {
// do what you'd have done for an empty list...
}