I have a form in my application that will insert information into the database, but its optional data, it can be nullable. I have this mapping:
case class TransactionFormData(id:Long, transID:Long, operatorID:Long, cardID:Long, stationID:Long, startTime:String, endTime:String, lineNumber:Int, cardNumber:Long, transNumber:Long, ticketNumber:Int, amountEntered:Int, amountMeasured:Int, leaseID_dpID:Int, customerID:Int)
val transForm = Form(
mapping(
"id" -> longNumber,
"transID" -> longNumber,
"operatorID" -> longNumber(min = 0),
"cardID" -> longNumber,
"stationID" -> longNumber,
"startTime" -> nonEmptyText,
"endTime" -> nonEmptyText,
"lineNumber" -> number(min = 0),
"cardNumber" -> longNumber(min = 0),
"transNumber" -> longNumber(min = 0),
"ticketNumber" -> OptionalMapping(number),
"amountEntered" -> number,
"amountMeasured" -> number,
"leaseID_dpID" -> number,
"customerID" -> number
)(TransactionFormData.apply)(TransactionFormData.unapply)
)
from ticketNumber on down, the values can be kept empty, but when I try putting nothing in, the form helper returns an error and requires input, how can I stop this?
Use the optional(number) constraint:
"ticketNumber" -> optional(number),
"amountEntered" -> optional(number),
"amountMeasured" -> optional(number),
"leaseID_dpID" -> optional(number),
"customerID" -> optional(number)
Just as number maps to Int, optional(number) maps to Some[Int]. The constraint allows the field to pass validation if no value is submitted in the form, assigning the field the value None. I appreciate however that this will then require you to change the signature of your case class constructor:
case class TransactionFormData(..., ticketNumber:Option[Int], ...)
Related
How to encode java.lang.Object using io.circe
Basically I am trying encode a Map
Map(
"key" -> Option[Json].map(_.asObject).getOrElse(Map.empty) // `object[a -> "x",b -> "y",c -> "z"]`
)
For e.g. I want to convert object[a -> "x",b -> "y",c -> "z"] to {"a":"x", "b":"y","c": "z"}
how would you go about pattern matching to extract values from Maps contained in a List ?
So my data is of type List[Map[String,Any]] and looks like :
List(Map(sequence -> 192, id -> 8697413670252052, type -> List(AimLowEvent, DiscreteEvent), time -> 527638582195))
List(Map(duration -> 143858743, id -> 8702168014834892, sequence -> 195, sessionId -> 8697344444103393, time -> 527780267698, type -> List(SessionCanceled, SessionEnded)), Map(trackingInfo -> Map(trackId -> 14170286, location -> Browse, listId -> cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585, videoId -> 80000778, rank -> 0, row -> 0, requestId -> ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171), id -> 8697344444103393, sequence -> 89, time -> 527636408955, type -> List(Play, Action, Session)), 1)
List(Map(duration -> 142862569, id -> 8702168403395215, sequence -> 201, sessionId -> 8697374208897843, time -> 527780267698, type -> List(SessionCanceled, SessionEnded)), Map(trackingInfo -> Map(trackId -> 14170286, location -> Browse, listId -> cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585, videoId -> 80000778, rank -> 0, row -> 0, requestId -> ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171), id -> 8697374208897843, sequence -> 136, time -> 527637405129, type -> List(Play, Action, Session)), 1)
Firstly I would only like to keep records that contain the Map(trackingInfo -> .. as the fields that are important to me are in those records, e.g trackId. However in those same records I need the outer fields also such as sequence
I've tired to flatten the list into a Map by, so I can match on the maps:
myList.flatten.toMap
However it returns a java.lang.ClassCastException: scala.collection.immutable.Map$Map4 cannot be cast to scala.Tuple2 error.
Any help is appreciated
For what you want to achieve, I believe a filter will do:
// taken from your example and simplified
val list =
List(
Map("duration" -> 142862569L, "id" -> 8702168403395215L, "sequence" -> 201, "sessionId" -> 8697374208897843L, "time" -> 527780267698L, "type" -> List("SessionCanceled", "SessionEnded")),
Map("trackingInfo" -> Map("trackId" -> 14170286L, "location" -> "Browse", "listId" -> "cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585", "videoId" -> "80000778", "rank" -> 0, "row" -> 0, "requestId" -> "ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171"), "id" -> 8697374208897843L, "sequence" -> 136, "time" -> 527637405129L, "type" -> List("Play", "Action", "Session")))
val onlyWithTrackingInfo =
list.filter(_.contains("trackingInfo"))
Printing onlyWithTrackingInfo would output the following (prettified):
List(
Map(
trackingInfo -> Map(trackId -> 14170286, location -> Browse, listId -> cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585, videoId -> 80000778, rank -> 0, row -> 0, requestId -> ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171),
id -> 8697374208897843,
sequence -> 136,
time -> 527637405129,
type -> List(Play, Action, Session)
)
)
One approach would be to collect any Map that consists of the wanted key:
def extract(list: List[Map[String, Any]], key: String) = list.collect{
case m if m.get(key) != None => m
}
val list1: List[Map[String, Any]] = List(
Map("sequence" -> 192, "id" -> 8697413670252052L, "type" -> List("AimLowEvent", "DiscreteEvent"), "time" -> 527638582195L)
)
val list2: List[Map[String, Any]] = List(
Map("duration" -> 143858743, "id" -> 8702168014834892L, "sequence" -> 195, "sessionId" -> 8697344444103393L, "time" -> 527780267698L, "type" -> List("SessionCanceled", "SessionEnded")),
Map("trackingInfo" -> Map("trackId" -> 14170286, "location" -> "Browse", "listId" -> "cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585", "videoId" -> 80000778, "rank" -> 0, "row" -> 0, "requestId" -> "ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171"), "id" -> 8697344444103393L, "sequence" -> 89, "time" -> 527636408955L, "type" -> List("Play", "Action", "Session"))
)
extract(list1, "trackingInfo")
// res1: List[Map[String,Any]] = List()
extract(list2, "trackingInfo")
// res2: List[Map[String,Any]] = List(
// Map(trackingInfo -> Map(trackId -> 14170286, location -> Browse, listId -> cd7c2c7a-00f6-4035-867f-d1dd7d89972d_6625365X3XX1505943605585, videoId -> 80000778, rank -> 0, row -> 0, requestId -> ac12f4e1-5644-46af-87d1-ec3b92ce4896-4071171), id -> 8697344444103393, sequence -> 89, time -> 527636408955, type -> List(Play, Action, Session))
// )
I'll begin with small code examples, made as simple as possible to sum up the problem.
Let's say I've got a function defined as follow :
func doSomething<T>(using f: (String, (T) -> Void) -> Void, key: String) {
f("test", {
object in
// do something with object using key
//...
})
}
I've got a bunch of auto generated classes with class methods, for exemple :
class MyClass {
var prop1: String = ""
}
class MyClassAPI {
class func f1(param: String, completion: (MyClass) -> Void) {
let mc = MyClass()
mc.prop1 = "hello"
completion(mc)
}
}
Then, I got a whole set of key/functions couples in a dictionary :
// Any because of cast problem !
let keyFunctions: [String: Any] = [
"first" : MyClassAPI.f1,
"second" : MySecondClassAPI.f2,
"third" : MyThirdClassAPI.f3
//...
]
Finally, i'll iterate through all the keys/function to call doSomething:
for (k, f) in keyFunctions {
// This line doesn't work as the compiler complain about typing errors
doSomething(using: f, key: k)
}
The problem I'm facing is that I can't cast my functions to the right type to pass them to doSomething :
Xcode suppose to force cast using f as! (String, (_) -> ()) -> Void then gives me an error, _ is not a type.
I tried to be more permissive using if let uf = f as? (String, (Any) -> Void) -> Voidwith no chance.
I read the whole generics pages of Swift manual without any hint on how to achieve this.
Please let me know of any existing way to perform such things using genericity.
Instead of Any make a more specific type for a block
let keyFunctions: [String: (String) -> Any]
Then, at least, your will compile and doSomething will be called.
However, there is no point in making it generic, as T will always be Any . If doSomething relies on common behaviour between your classes, then it would make sense to define a protocol for all of them.
If you actually want to have information about your classes, then you can introduce a non generic class that would keep this info:
class Wrap {
var resultType: Any.Type
let f: (String) -> Any
init<T>(_ f: #escaping (String) -> T) {
self.f = f
resultType = T.self
}
}
let keyFunctions: [String: Wrap] = [
"first" : Wrap(MyClassAPI.f1),
"second" : Wrap(MySecondClassAPI.f2)
]
But resultType can not be used in casting later though.
I finally made it this way :
func doSomething<T>(using f: (String, (Any) -> Void, key: String, type: T.Type) -> Void, key: String) {
f("test", {
object in
guard let o as? type else { return }
// do something with o using key
//...
})
}
let keyFunctions: [String: (String, (Any) -> Void)] = [
"first" : MyClassAPI.f1,
"second" : MySecondClassAPI.f2,
"third" : MyThirdClassAPI.f3
//...
]
When I set immutable hashmap contents on initialization like this:
var result_tags=HashMap[String,Int]()
result_tags=("video"->0,"game"->0,"news"->0,"ee"->0,"sport"->0,
"shop"->0,"ju"->0,"story"->0,"pho"->0,"con"->0,"live"->0,"life"->0,"soft"->0,"hire"->0,"car"->0,
"mm"->0,"mus"->0,"mob"->0,"male"->0,"heal"->0, "sca"->0,"bank"->0,"mail"->0,"cool"->0,"pict"->0, "dl"->0)
It gives me the error:
too many elements for tuple:26,allowed:22
It means the max number of tuples is 22. I know -> is for creating tuples. Is there other ways to initialize hashmap without the limit number of its elements.
What you're actually doing there is initializing a giant Tuple type and trying to assign it to the result_tags variable which is of type HashMap, which wouldn't work even if the tuple size wouldn't exceed
maximum size.
So the error you get regarding tuples is not referring to the -> syntax that you use, but to the number of elements inside the (...) list. You would get the same error even if you wrote it like this:
(("video", 0), ("game", 0), ..., ("dl", 0))
Second, in your case you should do:
var result_tags = HashMap("video" -> 0, "game" -> 0, ..., "dl" -> 0)
(notice that I omitted the type info because Scala infers the type for you.)
The (a1, a2, ..., aN) syntax is an entirely different thing in Scala, since it initializes a tuple type. Every such declaration is converted into a TupleN type, where the maximum size is 22. So the Scala library actually has 22 distinct Tuple classes from Tuple1 to Tuple22.
Third, your style could use some corrections
You should prefer the immutable version of map which is scala.collection.Map
You should prefer immutable variables, which means val instead of var
And less important, but variable names are preferred to be camelCased, so that would be resultTags
Just
var result_tags = Map("video"->0,"game"->0,"news"->0,"ee"->0,"sport"->0,
"shop"->0,"ju"->0,"story"->0,"pho"->0,"con"->0,"live"->0,"life"->0,"soft"->0,"hire"->0,"car"->0,
"mm"->0,"mus"->0,"mob"->0,"male"->0,"heal"->0, "sca"->0,"bank"->0,"mail"->0,"cool"->0,"pict"->0, "dl"->0)
This is how you initialise the Map in Scala
Map initialisation
import scala.collection.immutable.Map
val result_tags = Map("video" -> 0, "game" -> 0, "news" -> 0, "ee" -> 0, "sport" -> 0,
"shop" -> 0, "ju" -> 0, "story" -> 0, "pho" -> 0, "con" -> 0, "live" -> 0, "life" -> 0, "soft" -> 0, "hire" -> 0, "car" -> 0,
"mm" -> 0, "mus" -> 0, "mob" -> 0, "male" -> 0, "heal" -> 0, "sca" -> 0, "bank" -> 0, "mail" -> 0, "cool" -> 0, "pict" -> 0, "dl" -> 0)
HashMap initialisation
import scala.collection.immutable.HashMap
val result_tags = HashMap("video" -> 0, "game" -> 0, "news" -> 0, "ee" -> 0, "sport" -> 0,
"shop" -> 0, "ju" -> 0, "story" -> 0, "pho" -> 0, "con" -> 0, "live" -> 0, "life" -> 0, "soft" -> 0, "hire" -> 0, "car" -> 0,
"mm" -> 0, "mus" -> 0, "mob" -> 0, "male" -> 0, "heal" -> 0, "sca" -> 0, "bank" -> 0, "mail" -> 0, "cool" -> 0, "pict" -> 0, "dl" -> 0)
You could also use the + method in the immutable.HashMap class to achieve what you were trying to do:
From the scala doc:
def +[B1 >: B](elem1: (A, B1), elem2: (A, B1), elems: (A, B1)*): HashMap[A, B1]
Adds two or more elements to this collection and returns a new collection.
val result_tags = HashMap[String,Int]()
val result_tags_filled = result_tags + ("video"->0,"game"->0,"news"->0,"ee"->0,"sport"->0,
"shop"->0,"ju"->0,"story"->0,"pho"->0,"con"->0,"live"->0,"life"->0,"soft"->0,"hire"->0,"car"->0,
"mm"->0,"mus"->0,"mob"->0,"male"->0,"heal"->0, "sca"->0,"bank"->0,"mail"->0,"cool"->0,"pict"->0, "dl"->0)
Here is what I can do in Swift:
extension Int {
func square() -> Int { return self * self }
}
And then call it like this: 3.square(), that gives me 9. Also, i can do it like so: Int.square(3), and it will give me () -> (Int). So, Int.square(3)() gives 9.
But if I write let array = [1, 2, 3]; Array.map(array) it gives error Cannot convert value of type 'Array<Int>' to expected argument of type '[_]'
Question is, how I can use Array.map in that way?
EDIT
Ok, I'll try to explain my problem in details. Now, I have function like this:
func map<T, U>(f: T -> U) -> [T] -> [U] {
return { ts in
ts.map(f)
}
}
It works, but only on arrays. There are many types that have map function, and it not very nice to declare global function like that for every type. So, lets say there is type C that have map function C<T> -> (T -> U) -> C<U>
Also, lets say I have function f, that transform A -> B -> C into B -> A -> C.
So, it looks like I can do something like this:
let array = [1, 2, 3]
let square: Int -> Int = {$0 * $0}
map(square)(array) // [1, 4, 9], works fine
f(Array.map)(square)(array) // Error
Question is not about code readability but about how Swift's type system works.
Array.map function is defined as:
public func map<T>(self: [Self.Generator.Element]) -> (#noescape Self.Generator.Element throws -> T) rethrows -> [T]
The problem here is that the compiler cannot infer the return type of the transform function or T. So you have to define it the two following ways:
// variable declaration
let mapping: (Int -> Int) throws -> [Int] = Array.map(array)
// or (especially useful for further function calls)
aFunction(Array.map(array) as (Int -> Int) throws -> [Int])
You can also see that the map function is marked as rethrows which gets "translated" to throws if you use the function. (It looks like a bug but closures don't have rethrows which could be the reason for this behavior).
So the function f could look like this in order to use it with Array.map:
// where
// A is the array
// B is the function
// C the type of the returned array
func f<A, B, C>(f2: A -> (B throws -> C)) -> B -> (A throws -> C) {
return { b in
{ a in
try f2(a)(b)
}
}
}
// or with a forced try! so you don't have to use try
func f<A, B, C>(f2: A -> (B throws -> C)) -> B -> A -> C {
return { b in
{ a in
try! f2(a)(b)
}
}
}
// call f (use try if you use the first implementation)
let square: Int -> Int = {$0 * $0}
f(Array.map)(square)
In the example with square the compiler can infer the type of the expression. In other words:
let f = Int.square(3)
is the same as
let f:() -> Int = Int.square(3)
However, map is a generic function that is parameterized on the closure return type:
public func map<T>(#noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
Consequently, this generates an error because the compiler doesn't know what T is:
let f = Array<Int>.map([1, 2, 3])
However, you can explicitly tell it what T is like this:
let f: ((Int) throws -> Int) throws -> [Int] = Array.map([1, 2, 3])
try! f({$0 * $0})
I think that answers your first question about square and map. I don't completely understand the rest of your question about converting A -> B -> C to B -> A -> C. Maybe you can provide more info on what f would look like.