Scala, Lift | SHtml.select - how to put list from database - forms

I'm trying to put into form ( select ) some values from database:
val kateg = Kategoria.findAll.map(a => (a.id.toString , a.nazwa))
And next in form:
bind("entry", xhtml,
"kateg" -> SHtml.select(kateg, Empty, select ),
"temat" -> SHtml.text(temat, temat = _),
"opis" -> SHtml.textarea(opis, opis = _, "cols" -> "80", "rows" -> "8"),
"submit" -> SHtml.submit("Add", processEntryAdd))
And then i have error:
Description Resource Path Location Type
type mismatch; found : List[(java.lang.String, a.nazwa.type) for
Some { val a: code.model.Kategoria }]
required: Seq[(String, String)] Forma.scala
/lift-todo-mongo/src/main/scala/code/snippet
line 51 Scala Problem
any ideas ? Thanks

SHtml.select(..) allows you to choose a String value.
It takes a Seq of tuples (Value: String, Key: String)
In that case you probably need to write:
val kateg = Kategoria.findAll.map(a => (a.id.toString , a.nazwa.is))
if nazwa is MappedString field of Kategoria entity.
i.e. kateg should have a type of Seq[(String, String)]
But I would suggest you to use SHtml.selectObj to select Kategoria entity instead of String name value:
val kateg: Seq[(Kategoria, String)] = Kategoria.findAll.map(a => (a, a.nazwa.is))
SHtml.selectObj[Kategoria](kateg, Empty, (k: Kategoria) => { .. /* assign */ .. })

Related

Read Hocon config as a Map[String, String] with key in dot notation and value

I have following HOCON config:
a {
b.c.d = "val1"
d.f.g = "val2"
}
HOCON represents paths "b.c.d" and "d.f.g" as objects. So, I would like to have a reader, which reads these configs as Map[String, String], ex:
Map("b.c.d" -> "val1", "d.f.g" -> "val2")
I've created a reader and trying to do it recursively:
import scala.collection.mutable.{Map => MutableMap}
private implicit val mapReader: ConfigReader[Map[String, String]] = ConfigReader.fromCursor(cur => {
def concat(prefix: String, key: String): String = if (prefix.nonEmpty) s"$prefix.$key" else key
def toMap(): Map[String, String] = {
val acc = MutableMap[String, String]()
def go(
cur: ConfigCursor,
prefix: String = EMPTY,
acc: MutableMap[String, String]
): Result[Map[String, Object]] = {
cur.fluent.mapObject { obj =>
obj.value.valueType() match {
case ConfigValueType.OBJECT => go(obj, concat(prefix, obj.pathElems.head), acc)
case ConfigValueType.STRING =>
acc += (concat(prefix, obj.pathElems.head) -> obj.asString.right.getOrElse(EMPTY))
}
obj.asRight
}
}
go(cur, acc = acc)
acc.toMap
}
toMap().asRight
})
It gives me the correct result but is there a way to avoid MutableMap here?
P.S. Also, I would like to keep implementation by "pureconfig" reader.
The solution given by Ivan Stanislavciuc isn't ideal. If the parsed config object contains values other than strings or objects, you don't get an error message (as you would expect) but instead some very strange output. For instance, if you parse a typesafe config document like this
"a":[1]
The resulting value will look like this:
Map(a -> [
# String: 1
1
])
And even if the input only contains objects and strings, it doesn't work correctly, because it erroneously adds double quotes around all the string values.
So I gave this a shot myself and came up with a recursive solution that reports an error for things like lists or null and doesn't add quotes that shouldn't be there.
implicit val reader: ConfigReader[Map[String, String]] = {
implicit val r: ConfigReader[String => Map[String, String]] =
ConfigReader[String]
.map(v => (prefix: String) => Map(prefix -> v))
.orElse { reader.map { v =>
(prefix: String) => v.map { case (k, v2) => s"$prefix.$k" -> v2 }
}}
ConfigReader[Map[String, String => Map[String, String]]].map {
_.flatMap { case (prefix, v) => v(prefix) }
}
}
Note that my solution doesn't mention ConfigValue or ConfigReader.Result at all. It only takes existing ConfigReader objects and combines them with combinators like map and orElse. This is, generally speaking, the best way to write ConfigReaders: don't start from scratch with methods like ConfigReader.fromFunction, use existing readers and combine them.
It seems a bit surprising at first that the above code works at all, because I'm using reader within its own definition. But it works because the orElse method takes its argument by name and not by value.
You can do the same without using recursion. Use method entrySet as following
import scala.jdk.CollectionConverters._
val hocon =
"""
|a {
| b.c.d = "val1"
| d.f.g = val2
|}""".stripMargin
val config = ConfigFactory.load(ConfigFactory.parseString(hocon))
val innerConfig = config.getConfig("a")
val map = innerConfig
.entrySet()
.asScala
.map { entry =>
entry.getKey -> entry.getValue.render()
}
.toMap
println(map)
Produces
Map(b.c.d -> "val1", d.f.g -> "val2")
With given knowledge, it's possible to define a pureconfig.ConfigReader that reads Map[String, String] as following
implicit val reader: ConfigReader[Map[String, String]] = ConfigReader.fromFunction {
case co: ConfigObject =>
Right(
co.toConfig
.entrySet()
.asScala
.map { entry =>
entry.getKey -> entry.getValue.render()
}
.toMap
)
case value =>
//Handle error case
Left(
ConfigReaderFailures(
ThrowableFailure(
new RuntimeException("cannot be mapped to map of string -> string"),
Option(value.origin())
)
)
)
}
I did not want to write custom readers to get a mapping of key value pairs. I instead changed my internal data type from a map to list of pairs (I am using kotlin), and then I can easily change that to a map at some later internal stage if I need to. My HOCON was then able to look like this.
additionalProperties = [
{first = "sasl.mechanism", second = "PLAIN"},
{first = "security.protocol", second = "SASL_SSL"},
]
additionalProducerProperties = [
{first = "acks", second = "all"},
]
Not the best for humans... but I prefer it to having to build custom parsing components.

Replacing values from list of custom objects with map values

I have a quite odd problem to solve, I have a String, a custom Type and a Map of Maps.
The string needs to have a few values replaced based on mapping between a value in custom type (which is a key in the map of maps).
This is the current structure:
case class Students(favSubject: String)
val mapping: Map[String, Map[String, String]] = Map("John" -> Map("English" -> "Soccer"))
val studentInfo: List[Students] = List(Students("English"))
val data: String = "John is the favourite hobby"
I tried the following:
mapping.foldLeft(data){ case (outputString, (studentName, favSubject)) => outputString.replace(studentName, favSubject.getOrElse(studentInfo.map(x => x.favSubject).toString, "")) }
What I need to get is:
"Soccer is the favourite hobby"
What I get is:
" is the favourite hobby"
So looks like I am getting the map of maps traversal right but the getOrElse part is having issues.
What I would do, would be to first change the structure of mappings so it makes more sense for the problem.
val mapping: Map[String, Map[String, String]] = Map("John" -> Map("English" -> "Soccer"))
val mapping2 =
mapping.iterator.flatMap {
case (student, map) => map.iterator.map {
case (info, value) => (info, student, value)
}
}.toList
.groupBy(_._1)
.view
.mapValues { group =>
group.iterator.map {
case (_, student, value) => student -> value
}.toList
}.toMap
// mapping2: Map[String, List[(String, String)]] = Map("English" -> List(("John", "Soccer")))
Then I would just traverse the students informativo, making all the necessary replacements.
final case class StudentInfo(favSubject: String)
val studentsInformation: List[StudentInfo] = List(StudentInfo("English"))
val data: String = "John is the favourite hobby"
val result =
studentsInformation.foldLeft(data) { (acc, info) =>
mapping2
.getOrElse(key = info.favSubject, default = List.empty)
.foldLeft(acc) { (acc2, tuple) =>
val (key, replace) = tuple
acc2.replace(key, replace)
}
}
// result: String = "Soccer is the favourite hobby"
When you map() a List, you get a List back. It's toString has a format "List(el1,el2,...)". Surely you cannot use it as a key for your sub-map, you would want just el1.
Here is a version of the working code. It might not be a solution you are looking for(!), just a solution to your question:
case class Students(favSubject: String)
val mapping: Map[String, Map[String, String]] = Map("John" -> Map("English" -> "Soccer"))
val studentInfo: List[Students] = List(Students("English"))
val data: String = "John is the favourite hobby"
val res = mapping.foldLeft(data) {
case (outputString, (studentName, favSubjectDict)) =>
outputString.replace(
studentName,
favSubjectDict.getOrElse(studentInfo.map(x => x.favSubject).head, "?")
)
}
println(s"$res") //prints "Soccer is the favourite hobby"
val notMatchingSubject = studentInfo.map(x => x.favSubject).toString
println(s"Problem in previous code: '$notMatchingSubject' !== 'English'")
Try it here: https://scastie.scala-lang.org/flQNRrUQSXWPxSTXOPPFgA
The issue
It is a bit unclear why StudentInfo is a List in this form... If I guessed, it was designed to be a list of StudentInfo containing both, name and favSubject and you would need to search it by name to find favSubject. But it is just a guess.
I went with the simplest working solution, to get a .head (first element) of the sequence from the map. Which will always be "English" even if you add more Studends to the list.

scala: union of two maps whose key type is the same and whose value type is a collection of elements, but whose types are different

I would like to create a union of two maps whose key type is the same and whose value type is a collection of elements, but whose types are different.
Consider the following contrived example:
case class Child(name: String)
val peopleToChildren: Map[String, Seq[Child]] =
Map("max" -> Seq(Child("a"), Child("b")),
"yaneeve" -> Seq(Child("y"), Child("d")))
case class Pet(name: String)
val peopleToPets: Map[String, Seq[Pet]] =
Map("max" -> Seq(Pet("fifi")),
"jill" -> Seq(Pet("bobo"), Pet("jack"), Pet("Roger rabbit")))
val peopleToChildrenAndDogs: Map[String, (Seq[Child], Seq[Pet])] = {
// people may have children
// people may have pets
// would like a map from people to a tuple with a potentially empty list of children and a
// potentially empty list of pets
// ???
}
What would be a way to do it which is concise, idiomatic, but still legible?
I found no single function that can do that in the standard scala collections library.
Proposed solutions can be based solely on the standard library, or propose an external solution.
I post it here since I could not easily find an online solution to a seemingly standard operation.
This appears to work.
val peopleToChildrenAndDogs: Map[String, (Seq[Child], Seq[Pet])] = {
(peopleToChildren.keySet ++ peopleToPets.keySet).map { k =>
k -> (peopleToChildren.getOrElse(k, Seq())
,peopleToPets.getOrElse(k, Seq()))
}.toMap
}
Get all the keys. For every key do a getOrElse() on each of the feeder Maps.
Just for the curious, here's how it could be done using Scalaz:
import scalaz._, Scalaz._
case class Child(name: String)
val peopleToChildren = Map(
"max" -> List(Child("a"), Child("b")),
"yaneeve" -> List(Child("y"), Child("d"))
)
case class Pet(name: String)
val peopleToPets = Map(
"max" -> List(Pet("fifi")),
"jill" -> List(Pet("bobo"), Pet("jack"), Pet("Roger rabbit"))
)
val peopleToChildrenAndPets: Map[String, (List[Child], List[Pet])] =
peopleToChildren.strengthR(nil[Pet]) |+| peopleToPets.strengthL(nil[Child])
Explanation:
nil[Pet] is just an alias for List.empty[Pet]
strengthR for a given Functor tuples contained values, so that its parameter is at the right. Here it is equivalent to peopleToChildren.mapValues(v => (v, nil[Pet]))
strengthL is the same, but element will be added to the left
|+| is an append operator for a given Semigroup. The one here is derived recursively:
for Map[K, V], it uses |+| to combine values of type V if a given key exists in both Maps. If the value is only present in one of them, it will be retained as is. Here, V = (List[Child], List[Pet])
for tuples (A, B), it again uses |+| to combine both As and Bs. Here, A = List[Child] and B = List[Pet]
for lists of any type (as well as strings, vectors or streams) it does concatenation. This is why I had to change type of Map values to be Lists - for generic Seqs this operation is not defined
Result:
peopleToChildrenAndPets: Map[String, (List[Child], List[Pet])] = Map(
"max" -> (List(Child("a"), Child("b")), List(Pet("fifi"))),
"jill" -> (
List(),
List(Pet("bobo"), Pet("jack"), Pet("Roger rabbit"))
),
"yaneeve" -> (List(Child("y"), Child("d")), List())
)
To answer my own question, the following is the way that I solved it, but it seems overly long and complex:
Welcome to the Ammonite Repl 1.0.2
(Scala 2.11.11 Java 1.8.0_91)
If you like Ammonite, please support our development at www.patreon.com/lihaoyi
# case class Child(name: String)
defined class Child
# val peopleToChildren: Map[String, Seq[Child]] =
Map("max" -> Seq(Child("a"), Child("b")),
"yaneeve" -> Seq(Child("y"), Child("d")))
peopleToChildren: Map[String, Seq[Child]] = Map("max" -> List(Child("a"), Child("b")), "yaneeve" -> List(Child("y"), Child("d")))
#
# case class Pet(name: String)
defined class Pet
# val peopleToPets: Map[String, Seq[Pet]] =
Map("max" -> Seq(Pet("fifi")),
"jill" -> Seq(Pet("bobo"), Pet("jack"), Pet("Roger rabbit")))
peopleToPets: Map[String, Seq[Pet]] = Map("max" -> List(Pet("fifi")), "jill" -> List(Pet("bobo"), Pet("jack"), Pet("Roger rabbit")))
#
# val peopleToChildrenAndDogs: Map[String, (Seq[Child], Seq[Pet])] = {
// people may have children
// people may have pets
// would like a map from people to a tuple with a potentially empty list of children and a
// potentially empty list of pets
val paddedPeopleToChildren = peopleToChildren.map{ case (person, children) => person -> (children, List.empty[Pet])}
val paddedPeopleToPets = peopleToPets.map{ case (person, pets) => person ->(List.empty[Child], pets)}
val notGoodEnough = paddedPeopleToPets ++ paddedPeopleToChildren // this is here to show that it does not work since it overwrites the value of a key - Map(max -> (List(Child(a), Child(b)),List()), jill -> (List(),List(Pet(bobo), Pet(jack), Pet(Roger rabbit))), yaneeve -> (List(Child(y), Child(d)),List()))
val allSeq = paddedPeopleToPets.toSeq ++ paddedPeopleToChildren.toSeq
val grouped = allSeq.groupBy(_._1).mapValues(_.map { case (_, tup) => tup })
val solution = grouped.mapValues(_.unzip).mapValues {case (wrappedChildren, wrappedPets) => (wrappedChildren.flatten, wrappedPets.flatten)}
solution
}
peopleToChildrenAndDogs: Map[String, (Seq[Child], Seq[Pet])] = Map(
"yaneeve" -> (ArrayBuffer(Child("y"), Child("d")), ArrayBuffer()),
"max" -> (ArrayBuffer(Child("a"), Child("b")), ArrayBuffer(Pet("fifi"))),
"jill" -> (ArrayBuffer(), ArrayBuffer(Pet("bobo"), Pet("jack"), Pet("Roger rabbit")))
)

How to create pair of keys of Map

For exemple:
mylist: Map("Start" -> 2015-05-30T00:00:00.000Z, "Daily" -> 2015-06-02T00:00:00.000Z, "Hourly" -> 2015-06-03T08:00:00.000Z, "End" -> 2015-06-04T15:00:00.000Z)
I want to output as following format:
myout: List( ("Start" -> 2015-05-30T00:00:00.000Z, "Daily" -> 2015-06-02T00:00:00.000Z), ("Daily" -> 2015-06-02T00:00:00.000Z, "Hourly" -> 2015-06-03T08:00:00.000Z), ("Hourly" -> 2015-06-03T08:00:00.000Z, "End" -> 2015-06-04T15:00:00.000) )
OR
myout: List( ("Start", "Daily"), ("Daily", "Hourly"), ("Hourly", "End"))
Case 1: Always start with "Start" key, Anything comes before "Start" key ignore it. Same for last "End" key
mylist: Map(Hourly -> 2015-06-01T08:00:00.000Z, Start -> 2015-05-30T00:00:00.000Z, Daily -> 2015-06-02T00:00:00.000Z, End -> 2015-06-04T15:00:00.000Z, Weekly-> 2015-06-05T00:00:00.000Z)
output should like:
List((Start, Daily), (Daily, End))
I am looking output using scala.
import scala.collection.immutable.ListMap
val x = ListMap("Start" -> "x", "Daily" -> "y", "Hourly" -> "z", "End" -> "a")
x.toList.sliding(2).map( a => (a(0)._1, a(1)._1)).toList
List((Start,Daily), (Daily,Hourly), (Hourly,End))
Since a Map is not ordered, I have modified the input data to get stable results.
As for the 1st question
val m =
Map(
"1-Start" -> "2015-05-30T00:00:00.000Z",
"2-Daily" -> "2015-06-02T00:00:00.000Z",
"3-Hourly" -> "2015-06-03T08:00:00.000Z",
"4-End" -> "2015-06-04T15:00:00.000Z")
The basic idea is to zip the list of keys with its own tail to get the pairs:
scala> m.keys.toList.sorted.zip(m.keys.toList.sorted.tail)
res57: List[(String, String)] = List((1-Start,2-Daily), (2-Daily,3-Hourly),
(3-Hourly,4-End))
To simplify the expression a "pipe forward operator" is helpful:
object PipeForwardContainer {
implicit class PipeForward[T](val v: T) extends AnyVal {
def |>[R](f: T => R): R = {
f(v)
}
}
}
import PipeForwardContainer._
This operator provides a reference to the intermediate result. Therefore you can write:
scala> m.keys.toList.sorted |> { l => l.zip(l.tail) }
res97: List[(String, String)] = List((1-Start,2-Daily), (2-Daily,3-Hourly),
(3-Hourly,4-End))
As for the 2nd question
val m =
Map(
"1-Hourly" -> "2015-06-03T08:00:00.000Z",
"2-Start" -> "2015-05-30T00:00:00.000Z",
"3-Daily" -> "2015-06-02T00:00:00.000Z",
"4-End" -> "2015-06-04T15:00:00.000Z",
"5-Weekly"-> "2015-06-05T00:00:00.000Z")
To get the raw list you can slice out the relevant elements by index:
scala> m.keys.toList.sorted |> { l =>
l.slice(l.indexOf("2-Start"), l.indexOf("4-End") + 1) }
res96: List[String] = List(2-Start, 3-Daily, 4-End)
Again with zip to get the pairs:
scala> m.keys.toList.sorted |> { l =>
l.slice(l.indexOf("2-Start"), l.indexOf("4-End") + 1)
} |> { l => l.zip(l.tail) }
res98: List[(String, String)] = List((1-Start,2-Daily), (2-Daily,3-Hourly),
(3-Hourly,4-End))

How to properly map a tuple function across a Seq[A]?

I have a function that is attempting to build a JSON object containing a representation of multiple tuples that are stored in a Seq[A], where A is a (Loop, Option[User]). My code looks like so:
def loops = Action {
val page : Int = 1
val orderBy : Int = 1
val filter : String = ""
val jsonifyLoops : (Loop, Option[User]) => Map[String, String] = {
case (loop,user) =>
Map(
"name" -> loop.name,
"created_at" -> loop.createdAt.map(dateFormat.format).getOrElse(""),
"deleted_at" -> loop.deletedAt.map(dateFormat.format).getOrElse(""),
"user_name" -> user.map(_.name).getOrElse("")
)
}
Ok(toJson(Map(
"loops" -> toJson(
Loop.list( page = page, orderBy = orderBy, filter = ("%"+filter+"%") )
.items.map( jsonifyLoops )
)
)
))
}
Loops.list produces a Page[A], from the helper class below:
case class Page[A](items: Seq[A], page: Int, offset: Long, total: Long) {
lazy val prev = Option(page - 1).filter(_ >= 0)
lazy val next = Option(page + 1).filter(_ => (offset + items.size) < total)
}
Thus, Loops.list(...).items should get me a Seq[(Loop, Option[User])], onto which I should be able to apply a map function. I've defined my jsonifyLoops function to have what I think is the appropriate prototype, but I must be doing something wrong, because the compiler throws me the following error:
[error] [...] Application.scala:42: type mismatch;
[error] found : (models.Loop, Option[models.User]) => Map[String,String]
[error] required: (models.Loop, Option[models.User]) => ?
[error] .items.map( jsonifyLoops )
[error] ^
What am I doing wrong?
Your function jsonifyLoops takes two arguments: a Loop and an Option[User]. However, the members of items are tuples of the type (Loop, Option[User]), and thus items.map requires as an argument a function of one argument accepting that tuple. So, you need to convert jsonifyLoops from a binary function to a unary function that takes a pair of arguments; Function2#tupled will do this for you:
scala> :t jsonifyLoops
(Loop, Option[User]) => Map[String,String]
scala> :t jsonifyLoops.tupled
((Loop, Option[User])) => Map[String,String]
You'd use it like this:
Loop.list(page = page, orderBy = orderBy, filter = ("%"+filter+"%"))
.items.map(jsonifyLoops.tupled)
You need to add a default case to your pattern matching within jasonifyLoops.
In absence of a default case, if your case statement fails you return a Unit.
So something like this should work:
val jsonifyLoops : (Loop, Option[User]) => Map[String, String] = {
case (loop,user) =>
Map(
"name" -> loop.name,
"created_at" -> loop.createdAt.map(dateFormat.format).getOrElse(""),
"deleted_at" -> loop.deletedAt.map(dateFormat.format).getOrElse(""),
"user_name" -> user.map(_.name).getOrElse("")
)
case _ => Map[String, String]()
}
This just says that if the input does not match, return an empty Map. However, you should replace this with whatever handling you want to do for the default case.