how to fill case class with args from command line? - scala

If I've a case class like below
case class CommandArgs(args : Seq[String])
val argsOne
val argsTwo
val argsThree
And If I pass argsOne, argsTwo, argsThree from command line, how can I set these params values to CommandArgs's instance variables (mentioned in above class) ?

How about this?
case class CommandArgs(args : Seq[String]) {
val argsOne = args.lift(0).getOrElse(argOneDefault)
val argsTwo = args.lift(1).getOrElse(argTwoDefault)
val argsThree = args.lift(2).getOrElse(argThreeDefault)
}
But there is a better way that doesn't retain the original args in the resulting case class:
case class CommandArgs(arg1: String, arg2: String, arg3: String)
object CommandArgs {
def apply(args : Seq[String]): CommandArgs =
CommandArgs(
args.lift(0).getOrElse(argOneDefault)
args.lift(1).getOrElse(argTwoDefault)
args.lift(2).getOrElse(argThreeDefault)
)
}

Related

How to collect field values from case classes in scala

I've got various case classes with different fields inherit some trait. All are mixed in a List. What is the way to collect (or group by) specific field's values?
sealed trait Template
object Template {
case class TemplateA(field: String) extends Template
case class TemplateB extends Template
}
object Runner {
def main(args: String*) {
val list = List(TemplateA("abc"), TemplateB, Template("cde"))
// need to output something like "abc;1", "cde;1"
}
}
Totally agree with #LuisMiguel, just to show one way of doing this, here's what I can think of:
trait Template { val field: Option[String] }
case class TemplateA(field: Option[String]) extends Template
case class TemplateB() extends Template { override val field: Option[String] = None }
val list: List[Template] = List(
TemplateA(Some("abc")),
TemplateB(),
TemplateA(Some("cde"))
)
list.collect {
case template if template.field.nonEmpty =>
template.field.get
}.groupMapReduce(identity)(_ => 1)(_ + _)
// res8: Map[String, Int] = Map("abc" -> 1, "cde" -> 1)
Or if you want to get rid of the Optional argument when instantiating TemplateA instances, you can also do this:
case class TemplateA(private val value: String) extends Template {
override val field: Option[String] = Option(value)
}
val list: List[Template] = List(TemplateA("abc"), TemplateB(), TemplateA("cde"))
As #DmytroMitin mentioned, we can do a bit of refactoring to avoid using ifs in our collect function, I'd rather use some sort of unapply function, that can extract the field value of TemplateA instances:
object Template { // or any name as you wish
def unapply(t: Template): Option[String] = t match {
case TemplateA(Some(value)) => Option(value)
case _ => None
}
}
And then, we can use pattern matching:
list.collect {
case Template(field) => field
}.groupMapReduce(identity)(_ => 1)(_ + _)

compare case class fields with sub fields of another case class in scala

I have the following 3 case classes:
case class Profile(name: String,
age: Int,
bankInfoData: BankInfoData,
userUpdatedFields: Option[UserUpdatedFields])
case class BankInfoData(accountNumber: Int,
bankAddress: String,
bankNumber: Int,
contactPerson: String,
phoneNumber: Int,
accountType: AccountType)
case class UserUpdatedFields(contactPerson: String,
phoneNumber: Int,
accountType: AccountType)
this is just enums, but i added anyway:
sealed trait AccountType extends EnumEntry
object AccountType extends Enum[AccountType] {
val values: IndexedSeq[AccountType] = findValues
case object Personal extends AccountType
case object Business extends AccountType
}
my task is - i need to write a funcc Profile and compare UserUpdatedFields(all of the fields) with SOME of the fields in BankInfoData...this func is to find which fields where updated.
so I wrote this func:
def findDiff(profile: Profile): Seq[String] = {
var listOfFieldsThatChanged: List[String] = List.empty
if (profile.bankInfoData.contactPerson != profile.userUpdatedFields.get.contactPerson){
listOfFieldsThatChanged = listOfFieldsThatChanged :+ "contactPerson"
}
if (profile.bankInfoData.phoneNumber != profile.userUpdatedFields.get.phoneNumber) {
listOfFieldsThatChanged = listOfFieldsThatChanged :+ "phoneNumber"
}
if (profile.bankInfoData.accountType != profile.userUpdatedFields.get.accountType) {
listOfFieldsThatChanged = listOfFieldsThatChanged :+ "accountType"
}
listOfFieldsThatChanged
}
val profile =
Profile(
"nir",
34,
BankInfoData(1, "somewhere", 2, "john", 123, AccountType.Personal),
Some(UserUpdatedFields("lee", 321, AccountType.Personal))
)
findDiff(profile)
it works, but wanted something cleaner..any suggestions?
Each case class extends Product interface so we could use it to convert case classes into sets of (field, value) elements. Then we can use set operations to find the difference. For example,
def findDiff(profile: Profile): Seq[String] = {
val userUpdatedFields = profile.userUpdatedFields.get
val bankInfoData = profile.bankInfoData
val updatedFieldsMap = userUpdatedFields.productElementNames.zip(userUpdatedFields.productIterator).toMap
val bankInfoDataMap = bankInfoData.productElementNames.zip(bankInfoData.productIterator).toMap
val bankInfoDataSubsetMap = bankInfoDataMap.view.filterKeys(userUpdatedFieldsMap.keys.toList.contains)
(bankInfoDataSubsetMap.toSet diff updatedFieldsMap.toSet).toList.map { case (field, value) => field }
}
Now findDiff(profile) should output List(phoneNumber, contactPerson). Note we are using productElementNames from Scala 2.13 to get the filed names which we then zip with corresponding values
userUpdatedFields.productElementNames.zip(userUpdatedFields.productIterator)
Also we rely on filterKeys and diff.
A simple improvement would be to introduce a trait
trait Fields {
val contactPerson: String
val phoneNumber: Int
val accountType: AccountType
def findDiff(that: Fields): Seq[String] = Seq(
Some(contactPerson).filter(_ != that.contactPerson).map(_ => "contactPerson"),
Some(phoneNumber).filter(_ != that.phoneNumber).map(_ => "phoneNumber"),
Some(accountType).filter(_ != that.accountType).map(_ => "accountType")
).flatten
}
case class BankInfoData(accountNumber: Int,
bankAddress: String,
bankNumber: Int,
contactPerson: String,
phoneNumber: Int,
accountType: String) extends Fields
case class UserUpdatedFields(contactPerson: String,
phoneNumber: Int,
accountType: AccountType) extends Fields
so it was possible to call
BankInfoData(...). findDiff(UserUpdatedFields(...))
If you want to further-improve and avoid naming all the fields multiple times, for example shapeless could be used to do it compile time. Not exactly the same but something like this to get started. Or use reflection to do it runtime like this answer.
That would be a very easy task to achieve if it would be an easy way to convert case class to map. Unfortunately, case classes don't offer that functionality out-of-box yet in Scala 2.12 (as Mario have mentioned it will be easy to achieve in Scala 2.13).
There's a library called shapeless, that offers some generic programming utilities. For example, we could write an extension function toMap using Record and ToMap from shapeless:
object Mappable {
implicit class RichCaseClass[X](val x: X) extends AnyVal {
import shapeless._
import ops.record._
def toMap[L <: HList](
implicit gen: LabelledGeneric.Aux[X, L],
toMap: ToMap[L]
): Map[String, Any] =
toMap(gen.to(x)).map{
case (k: Symbol, v) => k.name -> v
}
}
}
Then we could use it for findDiff:
def findDiff(profile: Profile): Seq[String] = {
import Mappable._
profile match {
case Profile(_, _, bankInfo, Some(userUpdatedFields)) =>
val bankInfoMap = bankInfo.toMap
userUpdatedFields.toMap.toList.flatMap{
case (k, v) if bankInfoMap.get(k).exists(_ != v) => Some(k)
case _ => None
}
case _ => Seq()
}
}

Scala assign type a dynamic value

I'm pretty new to Scala and I'm wondering if it is possible to create a type dynamically in some way. In practice what I want to achieve is something like this:
trait BaseAB
case class A(value: String) extends BaseAB
case class B(value: String) extends BaseAB
def build(name: String, m: String): BaseAB = {
type t = name match {
case "A" => A
base "B" => B
}
new t(m)
}
You can just create new instances in you case clauses, like
case "A" => A(m)
case "B" => B(m)
or you can create partially applied function representing constructor and then provide value
def build(name: String, m: String): BaseAB = {
val construct = name match {
case "A" => A.apply _
case "B" => B.apply _
}
construct(m)
}
> build("A", "boo")
res25: BaseAB = A("boo")
Your code works almost as-is, but it's not because there is some sort of "type-valued runtime-defined variables". Instead, it works because there are companion objects called A and B that have methods apply(s: String): A and apply(s: String): B, and also both conform to type String => BaseAB:
trait BaseAB
case class A(value: String) extends BaseAB
case class B(value: String) extends BaseAB
def build(name: String, m: String): BaseAB = {
val t = name match {
case "A" => A
case "B" => B
}
t(m)
}
In this code snippet, the type of t is inferred to be String => BaseAB (possibly with some additional marker traits like Serializable).
If you are sure that there are only "A" and "B", you can also write it as
(if (name == "A") A else B)(m)
it works for the same reason.

getting serealizing case class with play json library

My case classes looks like this:
case class Person(personalInfo: PersonalInfo, bankInfo: BankInfo)
case class PersonalInfo(fname: String, lname: String)
case class BankInfo(atmCode: Int, creditCard: CreditCard)
case class CreditCard(number: Int, experationDate: String)
so to be able to get a person in my controller I added serealizer for person:
object PersonSerealizer {
implicit val PersonalInfoFormat: OFormat[PersonalInfo] = Json.format[PersonalInfo]
implicit val CreditCardFormat: OFormat[CreditCard] = Json.format[CreditCard]
implicit val BankInfoFormat: OFormat[BankInfo] = Json.format[BankInfo]
implicit val PersonFormat: OFormat[Person] = Json.format[Person]
}
in my controller I have a super simple action that looks like this:
i
mport serializers.PersonSerealizer._
def getBrothers(): Action[JsValue] = Action.async(parse.json) { request =>
request.body.validate[Person] match {
case JsSuccess(person, _) =>
brothersService.getBrothers(person) // this returns a List[Person]
.map(res => Future{Ok(res)})
case JsError(errors) => Future(BadRequest("Errors! " + errors.mkString))
}
}
but I get this error:
Error:(23, 81) No unapply or unapplySeq function found for class
BankInfo: / implicit val BankInfoFormat:
OFormat[BankInfo] = Json.format[BankInfo]
something is weird...from what I know it supposed to work
The order of defining your implicits seems to matter. Also, I think it is safer to use companion objects instead of defining them inside an arbitrary object.
case class PersonalInfo(fname: String, lname: String)
object PersonalInfo {
implicit val personalInfoJsonFormat = Json.format[PersonalInfo]
}
case class CreditCard(number: Int, experationDate: String)
object CreditCard {
implicit val creditCardJsonFormat = Json.format[CreditCard]
}
case class BankInfo(atmCode: Int, creditCard: CreditCard)
object BankInfo {
implicit val bankInfoJsonFormat = Json.format[BankInfo]
}
case class Person(personalInfo: PersonalInfo, bankInfo: BankInfo)
object Person {
implicit val personJsonFmt = Json.format[Person]
}
// Your action will be like this
def getBrothers() = Action.async(parse.json) { req =>
req.body.validate[Person] match {
case JsSuccess(p, _) =>
Future.successful(Ok(Json.toJson(service.getBrothers(p))))
case JsError(errors) =>
Future.successful(BadRequest(JsError.toJson(errors)))
}
}

Scala: How to access a class property dynamically by name?

How can I look up the value of an object's property dynamically by name in Scala 2.10.x?
E.g. Given the class (it can't be a case class):
class Row(val click: Boolean,
val date: String,
val time: String)
I want to do something like:
val fields = List("click", "date", "time")
val row = new Row(click=true, date="2015-01-01", time="12:00:00")
fields.foreach(f => println(row.getProperty(f))) // how to do this?
class Row(val click: Boolean,
val date: String,
val time: String)
val row = new Row(click=true, date="2015-01-01", time="12:00:00")
row.getClass.getDeclaredFields foreach { f =>
f.setAccessible(true)
println(f.getName)
println(f.get(row))
}
You could also use the bean functionality from java/scala:
import scala.beans.BeanProperty
import java.beans.Introspector
object BeanEx extends App {
case class Stuff(#BeanProperty val i: Int, #BeanProperty val j: String)
val info = Introspector.getBeanInfo(classOf[Stuff])
val instance = Stuff(10, "Hello")
info.getPropertyDescriptors.map { p =>
println(p.getReadMethod.invoke(instance))
}
}