I want to make a string representation of case class
case class Person(s:Student)
case class Student(name:String,value:String){
def toMyString(flag:Boolean):String= if(flag)s"${name} =${value}" else s"Hello${name}+${value}"
}
Person(Student("abc","def")).productIterator
I want to call toMyString from productIterator.
My actual use case has many elements in case class .
Just ask each iterator element if it is the target type.
Person(Student("abc","def")).productIterator.collect{
case x:Student => x.toMyString(true)
}
// res0: Iterator[String] = non-empty iterator (String: "abc=def")
There are 2 solutions for generating the String for the element:
1.Create a trait for elements that need to implement toMyString and the elements that need to generate string extends from it, and implement it, like:
trait MyString {
def toMyString(flag: Boolean): String
}
case class Student(name: String, value: String) extends MyString{
def toMyString(flag: Boolean): String = if (flag) s"${name} =${value}" else s"Hello${name}+${value}"
}
val result: List[String] = Person(Student("abc","def")).productIterator.collect{
case x: MyString => x.toMyString(true)
}
2. Use the reflection for this by get toMyString method by method name, this is a tricky way and not type safe, should think more about this, like:
val student = Student("abc", "def")
val res: Iterator[String] = Person(student).productIterator.map(i => {
val method = i.getClass.getDeclaredMethods.filter(i => i.getName.equals("toMyString")).headOption
method match {
case Some(m) =>
m.invoke(i, true.asInstanceOf[AnyRef])
case None =>
null
}
})
We need Iterator[Student] instead of Iterator[Any]
Person(Student("abc","def")).productIterator.asInstanceOf[Iterator[Student]]
Related
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)(_ + _)
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()
}
}
Let's say i have 2 cases classes:
case class Money(amount: Int, currency: String)
case class Human(name: String, money: Money)
is there a nice way to "translate" a list of strings to class Human? smth like:
def superMethod[A](params: List[String]): A = ???
val params: List[Any] = List("john", 100, "dollar")
superMethod(params) // => Human("john", Money(100, "dollar"))
so essentially i know type A only in runtime
UPDATE: i found ~ what i was looking for. it seems i can do it via shapeless. example i found in github
Here is an implementation that works for generic classes A.
It relies on runtime reflection (that is, a different TypeTag can be passed to the method at runtime). The following obvious conditions must be fulfilled in order to use this method:
A must be on the class path, or otherwise be loadable by the used class loader
TypeTag must be available for A at the call site.
The actual implementation is in the Deserializer object. Then comes a little demo.
The deserializer:
import scala.reflect.runtime.universe.{TypeTag, Type}
object Deserializer {
/** Extracts an instance of type `A` from the
* flattened `Any` constructor arguments, and returns
* the constructed instance together with the remaining
* unused arguments.
*/
private def deserializeRecHelper(
flattened: List[Any],
tpe: Type
): (Any, List[Any]) = {
import scala.reflect.runtime.{universe => ru}
// println("Trying to deserialize " + tpe + " from " + flattened)
// println("Constructor alternatives: ")
// val constructorAlternatives = tpe.
// member(ru.termNames.CONSTRUCTOR).
// asTerm.
// alternatives.foreach(println)
val consSymb = tpe.
member(ru.termNames.CONSTRUCTOR).
asTerm.
alternatives(0).
asMethod
val argsTypes: List[Type] = consSymb.paramLists(0).map(_.typeSignature)
if (tpe =:= ru.typeOf[String] || argsTypes.isEmpty) {
val h :: t = flattened
(h, t)
} else {
val args_rems: List[(Any, List[Any])] = argsTypes.scanLeft(
(("throwaway-sentinel-in-deserializeRecHelper": Any), flattened)
) {
case ((_, remFs), t) =>
deserializeRecHelper(remFs, t)
}.tail
val remaining: List[Any] = args_rems.last._2
val args: List[Any] = args_rems.unzip._1
val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)
val classMirror = runtimeMirror.reflectClass(tpe.typeSymbol.asClass)
val cons = classMirror.reflectConstructor(consSymb)
// println("Build constructor arguments array for " + tpe + " : " + args)
val obj = cons.apply(args:_*)
(obj, remaining)
}
}
def deserialize[A: TypeTag](flattened: List[Any]): A = {
val (a, rem) = deserializeRecHelper(
flattened,
(implicitly: TypeTag[A]).tpe
)
require(
rem.isEmpty,
"Superfluous arguments remained after deserialization: " + rem
)
a.asInstanceOf[A]
}
}
Demo:
case class Person(id: String, money: Money, pet: Pet, lifeMotto: String)
case class Money(num: Int, currency: String)
case class Pet(color: String, species: Species)
case class Species(description: String, name: String)
object Example {
def main(args: Array[String]): Unit = {
val data = List("Bob", 42, "USD", "pink", "invisible", "unicorn", "what's going on ey?")
val p = Deserializer.deserialize[Person](data)
println(p)
}
}
Output:
Person(Bob,Money(42,USD),Pet(pink,Species(invisible,unicorn)),what's going on ey?)
Discussion
This implementation is not restricted to case classes, but it requires each "Tree-node-like" class to have exactly one constructor that accepts either
primitive types (Int, Float), or
strings, or
other "Tree-node-like" classes.
Note that the task is somewhat ill-posed: what does it mean to say that all constructor arguments are flattened in a single list? Given the class Person(name: String, age: Int), will the List[Any] contain every single byte of the name as a separate entry? Probably not. Therefore, strings are handled by the deserializer in a special way, and all other collection-like entities are not supported for the same reasons (unclear where to stop parsing, because size of the collection is not known).
In case A is not a generic type, but effectively Human, you can use a companion object to the case class Human:
object Human {
def fromList(list: List[String]): Human = list match {
case List(name, amount, currency) => Human(name, Money(amount.toInt, currency))
case _ => handle corner case
}
}
Which you can call:
Human.fromList(List("john", "100", "dollar"))
To make it safe, don't forget to handle the case of lists whose size wouldn't be 3; and of lists whose 2nd element can't be cast to an Int:
import scala.util.Try
object Human {
def fromList(list: List[String]): Option[Human] = list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount.toInt, currency))).toOption
case _ => None
}
}
Edit: Based on your last comment, you might find this usefull:
case class Money(amount: Int, currency: String)
case class Human(name: String, money: Money)
case class SomethingElse(whatever: Double)
object Mapper {
def superMethod(list: List[String]): Option[Any] =
list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount.toInt, currency))).toOption
case List(whatever) => Try(SomethingElse(whatever.toDouble)).toOption
case _ => None
}
}
println(Mapper.superMethod(List("john", 100, "dollar")))
> Some(Human(john,Money(100,dollar)))
println(Mapper.superMethod(List(17d)))
> Some(SomethingElse(17.0))
or alternatively:
object Mapper {
def superMethod[A](list: List[String]): Option[A] =
(list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount, currency))).toOption
case List(whatever) =>
Try(SomethingElse(whatever.toDouble)).toOption
case _ => None
}).map(_.asInstanceOf[A])
}
println(Mapper.superMethod[Human](List("john", "100", "dollar")))
> Some(Human(john,Money(100,dollar)))
println(Mapper.superMethod[SomethingElse](List("17.2")))
> Some(SomethingElse(17.0))
I'm writing extractor object for functions expressions. Here is how it looks like:
object FunctionTemplate2 {
private final val pattern = Pattern.compile("^(.+?)\\((.+?)\\,(.+?)\\)")
//e.g. foo(1, "str_arg")
def unapply(functionCallExpression: String): Option[(String, String, String)] = {
//parse expression and extract
}
}
And I can extract as follows:
"foo(1, \"str_arg\")" match {
case FunctionTemplate2("foo", first, second) =>
println(s"$first,$second")
}
But this is not as cute as it could be. I would like to have something like that:
case FunctionTemplate2("foo")(first, second) =>
println(s"$first,$second")
Like curried extractor. So I tried this:
case class Function2Extractor(fooName: String){
private final val pattern = Pattern.compile("^(.+?)\\((.+?)\\,(.+?)\\)")
println("creating")
def unapply(functionCallExpression: String): Option[(String, String, String)] =
//parse and extract as before
}
But it did not work:
"foo(1, \"str_arg\")" match {
case Function2Extractor("foo")(first, second) =>
println(s"$first,$second")
}
Is there a way to do this in Scala?
You can simply it by using some utilities in Scala toolset
Notice how pattern is used in match case.
Scala REPL
scala> val pattern = "^(.+?)\\((.+?)\\,(.+?)\\)".r
pattern: scala.util.matching.Regex = ^(.+?)\((.+?)\,(.+?)\)
scala> "foo(1, \"str_arg\")" match { case pattern(x, y, z) => println(s"$x $y $z")}
foo 1 "str_arg"
I need to return values, and when someone asks for a value, tell them one of three things:
Here is the value
There is no value
We have no information on this value (unknown)
case 2 is subtly different than case 3. Example:
val radio = car.radioType
we know the value: return the radio type, say "pioneer"
b. there is no value: return None
c. we are missing data about this car, we don't know if it has a radio or not
I thought I might extend scala's None and create an Unknown, but that doesn't seem possible.
suggestions?
thanks!
Update:
Ideally I'd like to be able to write code like this:
car.radioType match {
case Unknown =>
case None =>
case Some(radioType : RadioType) =>
}
Here's a barebones implementation. You probably want to look at the source for the Option class for some of the bells and whistles:
package example
object App extends Application {
val x: TriOption[String] = TriUnknown
x match {
case TriSome(s) => println("found: " + s)
case TriNone => println("none")
case TriUnknown => println("unknown")
}
}
sealed abstract class TriOption[+A]
final case class TriSome[+A](x: A) extends TriOption[A]
final case object TriNone extends TriOption[Nothing]
final case object TriUnknown extends TriOption[Nothing]
Don't tell anyone I suggested this, but you could always use null for Unknown rather than writing a new class.
car.radioType match {
case null =>
case None =>
case Some(radioType : RadioType) =>
}
You can grab some stuff from Lift: the Box. It has three states, Full, Failure and Empty. Also, Empty and Failure both inherit from EmptyBox.
You can use scala.Either. Use Left for the exceptional value, and Right for the expected value which can be an Option in this case:
scala> type Result = Either[String, Option[String]]
defined type alias Result
scala> val hasValue: Result = Right(Some("pioneer"))
hasValue: Result = Right(Some(pioneer))
scala> val noValue: Result = Right(None)
noValue: Result = Right(None)
scala> val unknownValue = Left("unknown")
unknownValue: Left[java.lang.String,Nothing] = Left(unknown)
You could create your own with the three possibilities. Or as of one your car.radioType types you could have unknown, and then use guards on your case's to handle it.
If you roll your own, you should include the Product trait as well. liftweb has the Box type, which is an option close that allows for full, empty and erorr to happen.
I did something like similar to classify 3 types of lines in given file, a given line maybe, for instance, Float for header line, Long for a line in the middle (row), or String for the trailer line. Also isHeader, isRow and isTrailer can be used to know which one is. Hopefully helps:
sealed abstract class HRT[+H, +R, +T] {
val isHeader: Boolean
val isRow: Boolean
val isTrailer: Boolean
}
final case class Header[+H, +R, +T](h: H) extends HRT[H, R, T] {
override val isHeader: Boolean = true
override val isRow: Boolean = false
override val isTrailer: Boolean = false
}
final case class Row[+H, +R, +T](r: R) extends HRT[H, R, T] {
override val isHeader: Boolean = false
override val isRow: Boolean = true
override val isTrailer: Boolean = false
}
final case class Trailer[+H, +R, +T](t: T) extends HRT[H, R, T] {
override val isHeader: Boolean = false
override val isRow: Boolean = false
override val isTrailer: Boolean = true
}
object Demo {
def getEntries(): Seq[HRT[Float, Long, String]] =
List(
Header(3.14f),
Row(42),
Trailer("good bye")
)
val entries = getEntries()
entries.foreach {
case Header(f) => printf("header: %f\n", f)
case Row(l) => printf("row: %d\n", l)
case Trailer(s) => printf("trailer: %s\n", s)
}
}
With Scala 3 you can use union types like this:
object Unknown
object Empty
type MaybeRadio = RadioType | Empty.type | Unknown.type
car.radioType match
case Unknown => ???
case Empty => ???
case radioType: RadioType => ???
See https://docs.scala-lang.org/scala3/book/types-union.html