Map with different types to String - scala

I am trying to learn some functional programming in Scala.
I have this Map:
val params: Map[String, QueryMap] = Map(
"a" -> SimpleQueryVal("1"),
"b" -> ComplexQueryVal("2", "3")
)
Where QueryMap is (might not be the best approach):
sealed trait QueryMap
case class SimpleQueryVal(value: String) extends QueryMap
case class ComplexQueryVal(values: String*) extends QueryMap
My result would be having a string like query parameters: ?a=1&b=2&b=3
I tried something, but my method return an Iterator[String] even I use mkString, looks ugly and I am sure that there's a very simple way of doing it.
def paramString(queryMap: Map[String, QueryMap]) = queryMap.keys.map { key =>
val params = queryMap(key) match {
case SimpleQueryVal(x) => "%s=%s".format(key, x)
case complexQuery: ComplexQueryVal => complexQuery.values.map { value =>
"%s=%s".format(key, value)
}
}
val result: String = params match {
case s: String => s + "&"
case s: ArrayBuffer[_] => s.mkString("&")
}
result.mkString
}
I would appreciate any idea that would make me learn something for today. :)

I think the result String can be built in a simpler, more straight forward, manner.
def paramString(queryMap: Map[String, QueryMap]): String = queryMap.map{
case (k, sq: SimpleQueryVal) => s"$k=${sq.value}"
case (k, cq: ComplexQueryVal)=> cq.values.map(k + "=" + _).mkString("&")
}.mkString("&")

A little cleaner:
def paramString(queryMap: Map[String, QueryMap]) = queryMap.flatMap {
case (key, SimpleQueryVal(x)) => Seq(s"$key=$x")
case (key, ComplexQueryVal(values # _*)) => values.map {v =>
s"$key=$v"
}
}.mkString("&")
No need for ArrayBuffer or to repeat the .mkString("&").
Keep in mind that this is good for just learning. If you're actually trying to handle HTTP query string parameters, you need to URLEncode the keys and the values and there's probably better libraries for that.

Try this:
def paramString(queryMap: Map[String, QueryMap]) = {
val qParams = queryMap.keys.map { key =>
queryMap(key) match {
case SimpleQueryVal(x) => "%s=%s".format(key, x)
case complexQuery: ComplexQueryVal => complexQuery.values.map { value =>
"%s=%s".format(key, value)
}.mkString("&")
}
}
qParams.mkString("&")
}
println(paramString(params))
Here, first you get a Set[String] like a=1 or b=2&b=3. Then you simply do another .mkString("&") to concatenate them all.

Related

How to define an empty Tuple?

I have the following function definition:
private def extractUrl: String => (String, String)
= url =>
url
.split("/")
.toList
.filter(_.startsWith("localhost"))
.flatMap(e => e.split(":").toList)
.foldLeft[(String, String)](("", "")) { (acc, x) =>
acc match {
case ("", "") => (x, "")
case (a, "") => (a, x)
case z => z
}
}
the question is, is there another way to define an empty Tuple instead of ("", "")?
Empty tuple
("", "") is a tuple of empty strings with type (String, String).
Empty is unclear in that context, could be (None, None) or even (null, null) (bad)
You seem to use "" to represents a value that is not present. Try using None and Some[String], both sub types of Option[String], to indicate that a value is not present.
Analysis and comments
Potentially, your method seems not to do what is intended. (execute code below)
Think about using already present functions / methods / libraries for manipulating URLs (also see example below)
Think about using Option
object Fun {
import java.net.URL
def main(args: Array[String]): Unit = {
val url1 = "http://localhost:4000/a/b/c?x=1&y=2#asdf"
val url2 = "http://example.com:4000/a/localhostb/c?x=1&y=2#asdf"
val urls = List(url1, url2)
// your approach
println("Your approach")
urls.map( urlString => extractUrl(urlString ))
.foreach(println)
println("New approach")
urls.map(x => extractUrl2(x))
.filter( x => x.host.startsWith("localhost") )
.foreach(println)
}
case class HostPort(host: String, port: Option[String])
def extractUrl2: String => HostPort = urlString => {
val url = new URL(urlString)
HostPort(url.getHost,
url.getPort match {
case -1 => None
case i => Some(i.toString)
})
}
def extractUrl: String => (String, String) = url =>
url
.split("/")
.toList
.filter(_.startsWith("localhost"))
.flatMap(e => e.split(":").toList)
.foldLeft[(String, String)](("", "")) { (acc, x) =>
acc match {
case ("", "") => (x, "")
case (a, "") => (a, x)
case z => z
}
}
}
yields
Your approach
(localhost,4000)
(localhostb,)
New approach
HostPort(localhost,Some(4000))
I don't think it is possible to define an empty Tuple.
I tried to use (->) but that resolves to a Tuple2.type and not a Tuple2.
If the values of your Tuple are optional, use the type system to express that:
(Option[String], Option[String])
Better yet, you could define a case class for your data structure:
case class HostAndPort(host: Option[String], port: Option[String])
This would provide default values for each type within the tuple:
let my_tuple: (String, usize) = Default::default();

Optional parse from stream with State monad

I'm new to cats. I'm creating State instances to handle deserialisation of types from a byte stream. e.g.
val int: State[Seq[Byte], Int] = State[Seq[Byte], Int] {
case bs if bs.length >= 4 =>
bs.drop(4) -> ByteBuffer.wrap(bs.take(4).toArray).getInt
case _ => throw new EOFException()
}
I have implemented a parser of Option[Int] in terms of the above, like so:
val unit: State[Seq[Byte], Unit] = State[Seq[Byte], Unit](_ -> Unit)
val optInt: State[Seq[Byte], Option[Int]] = int.flatMap(i =>
if (i == 1) int.map(Some(_)) else unit.map(_ => None)
)
I feel that I've missed a trick here, as the implementation seems too verbose. Can I write this more succinctly? Can I do away with needing to define unit?
I wouldn't say that's too verbose, but I'd do two tricks with this:
Replace conditional with pattern matching function
Use State.pure instead of manually creating/transforming State values such as your unit.
val optInt: State[Seq[Byte], Option[Int]] = int.flatMap {
case 1 => int.map(Some(_))
case _ => State.pure(None)
}

Elegant way to validate scala map

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

Scala check a Sequence of Eithers

I want to update a sequence in Scala, I have this code :
def update(userId: Long): Either[String, Int] = {
Logins.findByUserId(userId) map {
logins: Login => update(login.id,
Seq(NamedParameter("random_date", "prefix-" + logins.randomDate)))
} match {
case sequence : Seq(Nil, Int) => sequence.foldLeft(Right(_) + Right(_))
case _ => Left("error.logins.update")
}
}
Where findByUserId returns a Seq[Logins] and update returns Either[String, Int] where Int is the number of updated rows,
and String would be the description of the error.
What I want to achieve is to return an String if while updating the list an error happenes or an Int with the total number of updated rows.
The code is not working, I think I should do something different in the match, I don't know how I can check if every element in the Seq of Eithers is a Right value.
If you are open to using Scalaz or Cats you can use traverse. An example using Scalaz :
import scalaz.std.either._
import scalaz.std.list._
import scalaz.syntax.traverse._
val logins = Seq(1, 2, 3)
val updateRight: Int => Either[String, Int] = Right(_)
val updateLeft: Int => Either[String, Int] = _ => Left("kaboom")
logins.toList.traverseU(updateLeft).map(_.sum) // Left(kaboom)
logins.toList.traverseU(updateRight).map(_.sum) // Right(6)
Traversing over the logins gives us a Either[String, List[Int]], if we get the sum of the List we get the wanted Either[String, Int].
We use toList because there is no Traverse instance for Seq.
traverse is a combination of map and sequence.
We use traverseU instead of traverse because it infers some of the types for us (otherwise we should have introduced a type alias or a type lambda).
Because we imported scalaz.std.either._ we can use map directly without using a right projection (.right.map).
You shouldn't really use a fold if you want to exit early. A better solution would be to recursively iterate over the list, updating and counting successes, then return the error when you encounter one.
Here's a little example function that shows the technique. You would probably want to modify this to do the update on each login instead of just counting.
val noErrors = List[Either[String,Int]](Right(10), Right(12))
val hasError = List[Either[String,Int]](Right(10), Left("oops"), Right(12))
def checkList(l: List[Either[String,Int]], goodCount: Int): Either[String, Int] = {
l match {
case Left(err) :: xs =>
Left(err)
case Right(_) :: xs =>
checkList(xs, (goodCount + 1))
case Nil =>
Right(goodCount)
}
}
val r1 = checkList(noErrors, 0)
val r2 = checkList(hasError, 0)
// r1: Either[String,Int] = Right(2)
// r2: Either[String,Int] = Left(oops)
You want to stop as soon as an update fails, don't you?
That means that you want to be doing your matching inside the map, not outside. Try is actually a more suitable construct for this purpose, than Either. Something like this, perhaps:
def update(userId: Long): Either[String, Int] = Try {
Logins.findByUserId(userId) map { login =>
update(login.id, whatever) match {
case Right(x) => x
case Left(s) => throw new Exception(s)
}
}.sum
}
.map { n => Right(n) }
.recover { case ex => Left(ex.getMessage) }
BTW, a not-too-widely-known fact about scala is that putting a return statement inside a lambda, actually returns from the enclosing method. So, another, somewhat shorter way to write this would be like this:
def update(userId: Long): Either[String, Int] =
Logins.findByUserId(userId).foldLeft(Right(0)) { (sum,login) =>
update(login.id, whatever) match {
case Right(x) => Right(sum.right + x)
case error#Left(s) => return error
}
}
Also, why in the world does findUserById return a sequence???

scala combinator parser to keep original input

I would like to compose a parser from another parser to have the consumed input as an argument to the ast construction.
Say I have
def ingredient = amount ~ nameOfIngredient ^^ {
case amount ~ name => Ingredient(name, amount)
}
What I'm looking for is a way to have another parser to construct an element of:
case class RecipeRow(orginalText: String, ingredient: Ingredient)
So I'm looking for a way to retrieve the original consumed input to the parser in a composition. Maybe something like:
def recipeRow = ingredient withConsumedInput ^^ {
case (amount ~ name, consumed) => RecipeRow(consumed, Ingredient(name, amount))
}
I guess the signature in this case would be:
def withConsumedInput [U](p: => Parser[U]): Parser[(U, String)]
Is there another simple way to get what I want or do I need to write that thing? It feels like it probably is a better way…
Not easy, actually.
Let's start with Parser: what can it give us? Well, a Parser extends Input => ParseResult, so we have to extract the information from either one.
The type Input is an alias, on RegexParsers anyway, for scala.util.parsing.input.Reader[Char]. There's very little there to help us, unless it happens to be a Reader of CharSequence, in which case we can use source and offset. Let's use that then.
Now, ParseResult has many subclasses, but we are only interested in Success, which has a next: Input field. Using that, we may try this:
def withConsumedInput [U](p: => Parser[U]): Parser[(U, String)] = new Parser[(U, String)] {
def apply(in: Input) = p(in) match {
case Success(result, next) =>
val parsedString = in.source.subSequence(in.offset, next.offset).toString
Success(result -> parsedString, next)
case other: NoSuccess => other
}
}
It will capture any skipped whitespaces, though. You can adapt it to avoid that automatically:
def withConsumedInput [U](p: => Parser[U]): Parser[(U, String)] = new Parser[(U, String)] {
def apply(in: Input) = p(in) match {
case Success(result, next) =>
val parsedString = in.source.subSequence(handleWhiteSpace(in.source, in.offset), next.offset).toString
Success(result -> parsedString, next)
case other: NoSuccess => other
}
}