How to create HashMap from a Vector in Scala - scala

I have this Vector:
val imageIds = Vector(
"XXXX1",
"XXXX2",
"XXXX3"
)
And I currently create an Array using the following method:
def makeTheImageDataArray: Vector[JsObject] = {
imageIds.map(SingleImageData(_, "theURL", "theStatus").asJsObject)
}
with this case class:
case class SingleImageData(ImageId: String, URL: String, Status: String) {
def imageId: String = ImageId
def getURL: String = URL
def status: String = Status
def asJsObject: JsObject = JsObject(
"ImageId" -> JsString(imageId),
"URL" -> JsString(getURL),
"Status" -> JsString(status)
)
}
Which produces:
Vector(
{"ImageId":"XXXX1","URL":"theURL","Status":"theStatus"},
{"ImageId":"XXXX2","URL":"theURL","Status":"theStatus"},
{"ImageId":"XXXX3","URL":"theURL","Status":"theStatus"}
)
Instead of producing a Vector, I want to create a HashMap instead, with the ImageId as the key, i.e. :
Map(
XXX1 -> {"URL":"theURL","Status":"theStatus"},
XXX2 -> {"URL":"theURL","Status":"theStatus"},
XXX3 -> {"URL":"theURL","Status":"theStatus"}
)
Can anyone show me how to do this?

Remove "ImageId" -> JsString(imageId) from asJsObject, then
imageIds.map(id => id -> SingleImageData(id, "theURL", "theStatus").asJsObject).toMap
or if SingleImageData doesn't need to know the id, remove ImageID entirely from SingleImageData, and
imageIds.map(_ -> SingleImageData("theURL", "theStatus").asJsObject).toMap

Related

How should I use JSONFindAndModifyCommand?

I have no idea how I should use play-reactivemongo's JSONFindAndModifyCommand.
I need to make an upsert query by some field. So I can first remove any existing entry and then insert. But Google says that FindAndModify command has upsert: Boolean option to achieve the same result.
Suppose I have two play.api.libs.json.JsObjects: query and object.
val q = (k: String) => Json.obj("sha256" -> k)
val obj = (k: String, v: String) => Json.obj(
"sha256" -> k,
"value" -> v
)
Then I do:
db.collection.findAndModify(
q(someSha256),
what?!,
...
)
I use play2-reactivemongo 0.11.9
Thanks!
The simpler is to use the collection operations findAndUpdate or findAndRemove, e.g.
val person: Future[BSONDocument] = collection.findAndUpdate( BSONDocument("name" -> "James"), BSONDocument("$set" -> BSONDocument("age" -> 17)), fetchNewObject = true) // on success, return the update document: // { "age": 17 }

Need to validate nested json arrays in play 2.1 in scala

I was able to perform simple validations on simple json structures like this one:
object RestTest extends Controller {
case class Address(street: String,
number: Int)
case class Person(name: String,
age: Int,
address: Address)
implicit val address = Json.reads[Address]
implicit val rds = Json.reads[Person]
def restTest = Action(parse.json) {
request =>
request.body.validate[Person].map {
case person => Ok(Json.obj("e" -> 0, "message" -> ("The name is: " + person.name + " and he lives in " + person.address.street)))
}.recoverTotal(e => Ok("e" -> 1)
}
}
Now I have the following structure that contains arrays, but I wasn't able to validate it correctly so far. I have tried many different ways, but I keep receiving compilation errors.
case class SecondStructure(index: Int)
case class EntryStructure(field1: String,
muSecondJsonArray: List[SecondStructure])
case class MyJsonArray(allEntries: List[EntryStructure])
How can I validate this json?
Thanks
First of all, ensure you are using the latest Play 2.1.1 releases. There was an issue with earlier versions when validating case classes with a single field. After that, it should all work - please see below for an example:
object JsonTest {
case class SecondStructure(index: Int)
case class EntryStructure(field1: String, muSecondJsonArray: List[SecondStructure])
case class MyJsonArray(allEntries: List[EntryStructure])
// Use the macro "inception" feature to automatically build your Readers.
implicit val ssReads = Json.reads[SecondStructure]
implicit val esReads = Json.reads[EntryStructure]
implicit val arrayReads = Json.reads[MyJsonArray]
// Defining an example instance...
val testArray = MyJsonArray(
List(
EntryStructure("foo", List(SecondStructure(1), SecondStructure(2))),
EntryStructure("bar", List(SecondStructure(3), SecondStructure(4)))))
// And the equivilant JSON structure...
val testJson = Json.obj("allEntries" ->
Json.arr(
Json.obj("field1" -> "foo", "muSecondJsonArray" -> Json.arr(
Json.obj("index" -> 1), Json.obj("index" -> 2))),
Json.obj("field1" -> "bar", "muSecondJsonArray" -> Json.arr(
Json.obj("index" -> 3), Json.obj("index" -> 4)))))
testJson.validate[MyJsonArray].map {
case foo if foo == testArray => println("Okay, we're good!")
}
}

Scala example of inputRadioGroup in Play2.1

I'm browsing around in the documentation but I can't find an example of how to use the inputRadioGroup in my Controller.
I guess i should use this helper. But how do I bind it to my form in my controller?
I would like to show a radio group that represents the grades 1 - 5
Controller:
object Sms extends Controller {
val testForm: Form[Test] = Form (
mapping(
"firstname" -> nonEmptyText,
"lastname" -> nonEmptyText,
"password" -> tuple(
"main" -> text(minLength = 6),
"confirm" -> text
).verifying(
"Passwords don't match", passwords => passwords._1 == passwords._2
),
"email" -> tuple(
"main" -> (text verifying pattern("^([0-9a-zA-Z]([-\\.\\w]*[0-9a-zA-Z])*#([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\\.)+[a-zA-Z]{2,9})$".r, error="A valid email is req")),
"confirm" -> text
).verifying(
"Emails don't match", emails => emails._1 == emails._2
),
"grade" -> Do the magic happen here?
)(Test.apply)(Test.unapply)
)
case class Test(
firstname: String,
lastname: String,
password: String,
email: String,
grade: Int
)
}
view:
#inputRadioGroup(
testForm("grade"),
options = Seq("1"->1,"2"->2....and so on)
'_label -> "Grade",
'_error -> testForm("grade").error.map(_.withMessage("some error")))
I can't figure out how to do this.
In your controller you create a Seq of the possible grades and pass the Seq to your view. I like using a case class Grade better then to pass a Tuple2[String, String] to the view. But I guess that's a matter of opinion.
case class Grade(value: Int, name: String)
private val grades = Seq(Grade(1, "Brilliant"), Grade(2, "Good"), Grade(3, "Ok"))
val testForm: Form[Test] = Form (...
"grade"-> number
)(Test.apply)(Test.unapply)
def edit(id: Long) = Action {
val model = ...obtain model
Ok(views.html.edit(testForm.fill(model), grades))
}
def submit() = Action {
testForm.bindFromRequest.fold(
formWithErrors => Ok(views.html.edit(formWithErrors, grades))
}, test => {
Logger.info("grade: " + grades.find(_.value == test.grade).map(_.name))
//save model...
Redirect(...
})
}
in your view, you map the grades Seq to a Tuple2[String, String] to feed inputRadioGroup
#(testForm: Form[Test], grades: Seq[Grade])
#inputRadioGroup(contactForm("grade"),
options = grades.map(g => g.value.toString -> g.name),
'_label -> "Grade")

Play! framework 2.0: Validate field in forms using other fields

In the play! framework, using scala,say that i have a form such as follows:
import play.api.data._
import play.api.data.Forms._
import play.api.data.validation.Constraints._
case class User(someStringField: String, someIntField: Int)
val userForm = Form(
mapping(
"someStringField" -> text,
"someIntField" -> number verifying(x => SomeMethodThatReceivesAnIntAndReturnsABoolean(x))
)(User.apply)(User.unapply)
)
where SomeMethodThatReceivesAnIntAndReturnsABoolean is a method that performs some logic on the int to validate it.
However, i would like to be able to consider the value of the someStringField when validating the someIntField, is there a way to achieve this in play framework's forms? I know that i can do something like:
val userForm = Form(
mapping(
"someStringField" -> text,
"someIntField" -> number
)(User.apply)(User.unapply)
.verifying(x => SomeFunctionThatReceivesAnUserAndReturnsABoolean(x))
and then i would have the entire user instance available passed to the validation function. The problem with that approach is that the resulting error would be associated with the entire form instead of being associated with the someIntField field.
Is there a way to get both things, validate a field using another field and maintain the error associated to the specific field i wish to validate, instead of the entire form?
I have the same requirements with adding validation to fields depending on the value of other fields. I´m not sure how this is done in idiomatic PLAY 2.2.1 but I came up with the following solution. In this usage I´m degrading the builtin "mapping" into a simple type converter and apply my "advanced inter field" validation in the "validateForm" method. The mapping:
val userForm = Form(
mapping(
"id" -> optional(longNumber),
"surename" -> text,
"forename" -> text,
"username" -> text,
"age" -> number
)(User.apply)(User.unapply)
)
private def validateForm(form:Form[User]) = {
if(form("username").value.get == "tom" || form("age").value.get == "38") {
form
.withError("forename", "tom - forename error")
.withError("surename", "tom - surename error")
}
else
form
}
def update = Action { implicit request =>
userForm.bindFromRequest.fold({
formWithErrors => BadRequest(users.edit(validateForm(formWithErrors)))
}, { user =>
val theForm = validateForm(userForm.fill(user))
if(theForm.hasErrors) {
BadRequest(users.edit(theForm))
} else {
Users.update(user)
Redirect(routes.UsersController.index).flashing("notice" -> s"${user.forename} updated!")
}
})
}
Even though it works I´m urgently searching for a more idiomatic version...
EDIT: Use a custom play.api.data.format.Formatter in idiomatic play, more on http://workwithplay.com/blog/2013/07/10/advanced-forms-techniques/ - this lets you programmatically add errors to a form. My Formatter looks like this:
val usernameFormatter = new Formatter[String] {
override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], String] = {
// "data" lets you access all form data values
val age = data.get("age").get
val username = data.get("username").get
if(age == "66") {
Left(List(FormError("username", "invalid"), FormError("forename", "invalid")))
} else {
Right(username)
}
}
override def unbind(key: String, value: String): Map[String, String] = {
Map(key -> value)
}
}
}
Registered in the form mapping like this:
mapping(
[...]
"username" -> of(usernameFormatter),
[....]
I believe what you're looking for is play.api.data.validation.Constraint.
Say you have a RegisterForm with a list of predefined cities and an otherCity field and you need either the cities or otherCity to be supplied, i.e., otherCity should be validated if cities is not provided:
case class RegisterForm(
email: String,
password: String,
cities: Option[List[String]],
otherCity: Option[String]
)
You can write a custom Constraint around this:
val citiesCheckConstraint: Constraint[RegisterForm] = Constraint("constraints.citiescheck")({
registerForm =>
// you have access to all the fields in the form here and can
// write complex logic here
if (registerForm.cities.isDefined || registerForm.otherCity.isDefined) {
Valid
} else {
Invalid(Seq(ValidationError("City must be selected")))
}
})
And your form definition becomes:
val registerForm = Form(
mapping(
"email" -> nonEmptyText.verifying(emailCheckConstraint),
"password" -> nonEmptyText.verifying(passwordCheckConstraint),
"cities" -> optional(list(text)),
"other_city" -> optional(text)
)(RegisterForm.apply)(RegisterForm.unapply).verifying(citiesCheckConstraint)
)
In this example emailCheckConstraint and passwordCheckConstraint are additional custom constraints that I defined similar to citiesCheckConstraint. This works in Play 2.2.x.
UPDATE:
Works on Play 2.3.8 as well.
if you don't mind having a prefix for you params you can group the related params:
val aForm = Form(
mapping(
"prefix" -> tuple(
"someStringField" -> text,
"someIntField" -> number
) verifying (tup => your verification)
)(tup => User.apply(tup._1, tup._2)(User.unapply...)
I use something similar just without the surrounding mapping.
You will have to adjust the apply/unapply a little and pass the arguments manually for it to compile.
The error will be registered to the "prefix" group.
I also find it weird that you cannot register errors on any field you'd like using FormError when verifying the form...
Thanks to Tom Myer, Here what I used
class MatchConstraint[A](val targetField:String, val map:(String, Map[String, String]) => A, val unmap:A => String) extends Formatter[A] {
override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], A] = {
val first = data.getOrElse(key, "")
val second = data.getOrElse(targetField, "")
if (first == "" || !first.equals(second)) {
Left(List(FormError(key, "Not Match!")))
}
else {
Right(map(key, data))
}
}
override def unbind(key: String, value: A): Map[String, String] = Map(key -> unmap(value))
}
And here what's my form look like
val registerForm = Form(
mapping(
"email" -> email.verifying(minLength(6)),
"password" -> text(minLength = 6),
"passwordConfirmation" -> of(new MatchConstraint[String]("password", (key, data) => data.getOrElse(key, ""), str => str))
)(RegisterData.apply)(RegisterData.unapply)
)
I guess that they map the scala-code to JSR-Validation. There it's definitely not possible. There are some arguments to do this. Mainly that a validation should be simple and not make complex logic. How ever I still miss this too. OVal from play1 was better for me.
In the documentation:
Playframework Documentation
You can see the following code:
val userFormConstraintsAdHoc = Form(
mapping(
"name" -> text,
"age" -> number
)(UserData.apply)(UserData.unapply) verifying("Failed form constraints!", fields => fields match {
case userData => validate(userData.name, userData.age).isDefined
})
)
Mainly just verify after the unapply and you have all the fields mapped, so you can make a more complete validation.

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

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 */ .. })