Related
I've created three classes A,B,C and in each class contains a list of elements , each class also contains a method that prints the elements , I've made a function outside the classes which has a pattern matching to choose which class to Print which takes a parameter of a list of the objects of the classes , my code is working well and can choose which class to print , but my question is what if the order of the objects of the classes in the list is not a,b,c but let's say c,a,b , how can someone then choose to print class A without knowing the order but just typing a ?
object printClass {
class A{
val a:List[Int] = List(1,2,3,4)
def printA(): Unit ={
println("Class A")
println(a)
}
}
class B{
val b:List[String] = List("Adam","Ali","Sara","Yara")
def printB(): Unit ={
println("Class B")
println(b)
}
}
class C{
val c:List[Char] = List('A','S','C','E')
def printC(): Unit ={
println("Class C")
println(c)
}
}
def prtClass(ch:Any): Unit ={
val a = new A()
val b = new B()
val c = new C()
ch match {
case a: A => a.printA()
case b: B => b.printB()
case c: C => c.printC()
case _ => print("Class not found")
}
}
def main(args: Array[String]): Unit = {
val a = new A()
val b = new B()
val c = new C()
val listOfObjects = List(a,b,c)
println("Choose the class to print (A,B,C) : ")
val choice:Int = scala.io.StdIn.readInt()
val ch = listOfObjects(choice)
prtClass(ch)
}
}
TLDR;
Use Map
Instead of using List to store a, b, c objects you could use Map. Keys as letters 'a' , 'b' , 'c' and values as objects a, b, c
val objects: Map[Char, Any] = Map('a' -> a, 'b' -> b, 'c' -> c)
And parse user input as Char
val choice = scala.io.StdIn.readChar()
Now now rest should fall in place. Objects will be fetched based on their association and same will be passed to prtClass function.
You could also define a parent class or trait to your A,B,C classes, so that Map value type can be confined to those types.
case class A(a:Int ,b:Int,c:Int,d:Int)
case class B(a:Int ,b:Int,c:Int,d:Int,e:List[Int],f:List[Int])
val a = A(1,2,3,4)
val b = B(a.a,a.b,a.c,a.d,List(1,2),List(2,3))
Currently, I am manually copying class A object to B like a.a, a.b, a.c, a.d
Is there any alternate way to do something like
val b = B(a.attributes.toList,List(1,2),List(2,3))
If you have access and control of the B code then you can add as many different constructors as you like.
case class A(a:Int, b:Int, c:Int, d:Int)
case class B(a:Int ,b:Int,c:Int,d:Int,e:List[Int],f:List[Int])
object B {
def apply(a:A, e:List[Int], f:List[Int]) = new B(a.a, a.b, a.c, a.d, e, f)
}
val a = A(1,2,3,4)
val b1 = B(a.a, a.b, a.c, a.d, List(1,2), List(2,3))
val b2 = B(a, List(4,5), List(9,1))
If you can't, or would rather not, modify A or B then you might add one or more implicit conversion methods.
implicit class A2B(a:A) {
def toB(e:List[Int], f:List[Int]) :B = B(a.a, a.b, a.c, a.d, e, f)
}
val a = A(1,2,3,4)
val b1 = B(a.a, a.b, a.c, a.d, List(1,2), List(2,3))
val b3 = a.toB(List(32,12), List(544,2))
There are some Scala libraries that focus on typesafe, boilerplate-free copying between case classes. I like Chimney:
https://scalalandio.github.io/chimney/
You can do as follows:
case class A(a:Int,b:Int,c:Int,d:Int)
case class B(a:Int,b:Int,c:Int,d:Int,e:List[Int],f:List[Int])
val a = A(1,2,3,4)
// if additional parameters e and f in B would have default values
val b1 = a.transformInto[B]
// explicitly set additional parameters in B to constant values
val b2 = a.into[B]
.withFieldConst(_.e, List(1,2))
.withFieldConst(_.f, List(1,2))
.transform
try this
`
case class A(a:Int, b:Int, c:Int, d:Int)
case class B(a:List[Any], e:List[Int], f:List[Int])
val a = A(1,2,3,4)
val b = B(a.productIterator.toList,List(1,2),List(2,3))
`
Try This
case class A(a:Int, b:Int, c:Int, d:Int)
case class B(a:A, e:List[Int], f:List[Int])
val a = A(1,2,3,4)
val b = B(a,List(1,2),List(2,3))
Recently I have come across a very useful groupBy function that Groovy has made available on Iterable:
public static Map groupBy(Iterable self, List<Closure> closures)
Which you can use to perform recursive groupBy on Lists and even Maps see example by mrhaki here
I would like to write a function that does the same in Scala. But having just started my Scala journey, I am kind of lost on how I should going about defining and implementing this method. Especially the generics side of the functions and return type on this method's signature are way beyond my level.
I would need more experienced Scala developers to help me out here.
Is this following signature totally wrong or am I in the ball park?
def groupBy[A, K[_]](src: List[A], fs: Seq[(A) ⇒ K[_]]): Map[K[_], List[A]]
Also, how would I implement the recursion with the correct types?
This is simple multigroup implementation:
implicit class GroupOps[A](coll: Seq[A]) {
def groupByKeys[B](fs: (A => B)*): Map[Seq[B], Seq[A]] =
coll.groupBy(elem => fs map (_(elem)))
}
val a = 1 to 20
a.groupByKeys(_ % 3, _ % 2) foreach println
If you really need some recursive type you'll need a wrapper:
sealed trait RecMap[K, V]
case class MapUnit[K, V](elem: V) extends RecMap[K, V] {
override def toString = elem.toString()
}
case class MapLayer[K, V](map: Map[K, RecMap[K, V]]) extends RecMap[K, V] {
override def toString = map.toString()
}
out definition changes to:
implicit class GroupOps[A](coll: Seq[A]) {
def groupByKeys[B](fs: (A => B)*): Map[Seq[B], Seq[A]] =
coll.groupBy(elem => fs map (_(elem)))
def groupRecursive[B](fs: (A => B)*): RecMap[B, Seq[A]] = fs match {
case Seq() => MapUnit(coll)
case f +: fs => MapLayer(coll groupBy f mapValues {_.groupRecursive(fs: _*)})
}
}
and a.groupRecursive(_ % 3, _ % 2) yield something more relevant to question
And finally i rebuild domain definition from referred article:
case class User(name: String, city: String, birthDate: Date) {
override def toString = name
}
implicit val date = new SimpleDateFormat("yyyy-MM-dd").parse(_: String)
val month = new SimpleDateFormat("MMM").format (_:Date)
val users = List(
User(name = "mrhaki", city = "Tilburg" , birthDate = "1973-9-7"),
User(name = "bob" , city = "New York" , birthDate = "1963-3-30"),
User(name = "britt" , city = "Amsterdam", birthDate = "1980-5-12"),
User(name = "kim" , city = "Amsterdam", birthDate = "1983-3-30"),
User(name = "liam" , city = "Tilburg" , birthDate = "2009-3-6")
)
now we can write
users.groupRecursive(_.city, u => month(u.birthDate))
and get
Map(Tilburg -> Map(Mar -> List(liam), Sep -> List(mrhaki)), New York
-> Map(Mar -> List(bob)), Amsterdam -> Map(Mar -> List(kim), May -> List(britt)))
I decided add another answer, due to fully different approach.
You could, actually get non-wrapped properly typed maps with huge workarounds. I not very good at this, so it by the chance could be simplified.
Trick - is to create Sequence of typed functions, which is lately producing multi-level map using type classes and type path approach.
So here is the solution
sealed trait KeySeq[-V] {
type values
}
case class KeyNil[V]() extends KeySeq[V] {
type values = Seq[V]
}
case class KeyCons[K, V, Next <: KeySeq[V]](f: V => K, next: Next)
(implicit ev: RecGroup[V, Next]) extends KeySeq[V] {
type values = Map[K, Next#values]
def #:[K1](f: V => K1) = new KeyCons[K1, V, KeyCons[K, V, Next]](f, this)
}
trait RecGroup[V, KS <: KeySeq[V]] {
def group(seq: Seq[V], ks: KS): KS#values
}
implicit def groupNil[V]: RecGroup[V, KeyNil[V]] = new RecGroup[V, KeyNil[V]] {
def group(seq: Seq[V], ks: KeyNil[V]) = seq
}
implicit def groupCons[K, V, Next <: KeySeq[V]](implicit ev: RecGroup[V, Next]): RecGroup[V, KeyCons[K, V, Next]] =
new RecGroup[V, KeyCons[K, V, Next]] {
def group(seq: Seq[V], ks: KeyCons[K, V, Next]) = seq.groupBy(ks.f) mapValues (_ groupRecursive ks.next)
}
implicit def funcAsKey[K, V](f: V => K): KeyCons[K, V, KeyNil[V]] =
new KeyCons[K, V, KeyNil[V]](f, KeyNil[V]())
implicit class GroupOps[V](coll: Seq[V]) {
def groupRecursive[KS <: KeySeq[V]](ks: KS)(implicit g: RecGroup[V, KS]) =
g.group(coll, ks)
}
key functions are composed via #: right-associative operator
so if we define
def mod(m:Int) = (x:Int) => x % m
def even(x:Int) = x % 2 == 0
then
1 to 30 groupRecursive (even _ #: mod(3) #: mod(5) )
would yield proper Map[Boolean,Map[Int,Map[Int,Int]]] !!!
and if from previous question we would like to
users.groupRecursive(((u:User)=> u.city(0)) #: ((u:User) => month(u.birthDate)))
We are building Map[Char,Map[String,User]] !
OK, I must be struggling with something stupid here (new to Scala), in which case apologies if it is something trivial.
I have a class Action which takes a name (irrelevant for the problem) and a list parameters.
Each parameter has a set of possible values, and I want to generate the full list of possible arguments for the parameters of the Action. I have been trying to use a for-comprehension but rather than filling in all arguments, I am getting combinations with just one value replaced.
My code looks something like this (I simplified it just to illustrate the problem):
case class ParamType(name: String)
{
def matchesType(that: ParamType): Boolean =
this.name == that.name
}
sealed abstract class Param (paramType : ParamType)
case class VarParam(parameter : String, paramType : ParamType) extends Param (paramType)
case class ValueArg(name : String, paramType : ParamType) extends Param(paramType)
case class Action(name: String, params: List[Param])
{
def possibleActions(possibleValues : Set[ValueArg]) : Set[Action] =
for {
parameter <- params.collect({case pt: VarParam => pt})
argument <- possibleValues
if (argument.matchesType(parameter))
} yield replaceParam(parameter, argument)
def replaceParam(parameter: VarParam, argument: ValueArg) : Action =
{
val i = parameters.indexOf(parameter)
if (i >= 0)
Action(name, parameters.updated(i, argument)
}
}
object tester {
def main(args: Array[String]) {
val possibleValues = Set[ValueArg](new ValueArg("a1", new ParamType("Atype")),
new ValueArg("a2", new ParamType("Atype")),
new ValueArg("a3", new ParamType("Atype")),
new ValueArg("b1", new ParamType("Btype")),
new ValueArg("b2", new ParamType("Btype")),
new ValueArg("b3", new ParamType("Btype")),
new ValueArg("c1", new ParamType("Ctype")),
new ValueArg("c2", new ParamType("Ctype")),
new ValueArg("c3", new ParamType("Ctype")))
val action1 = new Action("action1", List[Param](new VarParam("A", new ParamType("Atype")), new VarParam("B", new ParamType("Btype")), new VarParam("C", new ParamType("Ctype"))))
val possibleActions = action1.possibleActions(possibleValues)
println(possibleActions)
}
}
The function replaceParam just returns an action with the parameter replaced by the argument.
So if I have action1 with parameters A, B, C, each of which can take {a1, a2, a3}, {b1, b2, b3} and {c1, c2, c3} (assume they are of the same type), I am ending up with:
action1(a1, B, C) , action1(A, b1, C), action1(A, B, c1), action1(a2, B, C) etc.
But what I want is:
action1(a1, b1, c1), action1(a1, b1, c2), action1(a2, b1, c1) etc.
What is the most elegant way to achieve this?
The following is a simple recursive solution to your problem. However, note that passing the possible values as a Set might turn out to be problematic because this way there is no order on the possible values and hence all (and not only one in particular) corresponding parameters will iterate over all values with suitable parameter types. Now this should do what you asked:
case class ParamType(name: String) {
def matchesType(that: ParamType): Boolean =
this.name == that.name
}
sealed abstract class Param(val paramType: ParamType)
case class VarParam(parameter: String, override val paramType: ParamType) extends Param(paramType)
case class ValueArg(name: String, override val paramType: ParamType) extends Param(paramType)
case class Action(name: String, params: List[Param]) {
lazy val varParams: List[Param] =
params collect {
case pt: VarParam => pt
}
def possibleActions(possibleValues: Set[ValueArg]): Set[Action] = {
val pvs = possibleValues groupBy (_.paramType)
def aux(acc: List[Param], rem: List[Param]): Set[Action] = rem match {
case Nil => Set(Action(name, acc.reverse))
case x :: xs => for {
pv <- pvs(x.paramType) if pvs.contains(x.paramType)
rest <- aux(pv :: acc, xs)
} yield rest
}
aux(Nil, varParams)
}
}
val possibleValues = Set(
ValueArg("a1", ParamType("Atype")),
ValueArg("a2", ParamType("Atype")),
ValueArg("a3", ParamType("Atype")),
ValueArg("b1", ParamType("Btype")),
ValueArg("b2", ParamType("Btype")),
ValueArg("b3", ParamType("Btype")),
ValueArg("c1", ParamType("Ctype")),
ValueArg("c2", ParamType("Ctype")),
ValueArg("c3", ParamType("Ctype")))
val action1 = Action("action1",
List(VarParam("A", ParamType("Atype")),
VarParam("B", ParamType("Btype")),
VarParam("C", ParamType("Ctype"))))
val possibleActions = action1.possibleActions(possibleValues)
assert(possibleActions.size == 3 * 3 * 3)
I'm building a web-application using Play and Slick, and find myself in a situation where the user-facing forms are similar, but not exactly the same as the database model.
Hence I have two very similar case classes, and need to map from one to another (e.g. while filling the form for rendering an "update" view).
In the case I'm interested in, the database model case class is a super-set of the form case-class, i.e. the only difference between both is that the database model has two more fields (two identifiers, basically).
What I'm now wondering about is whether there'd be a way to build a small library (e.g. macro-driven) to automatically populate the form case class from the database case class based on the member names. I've seen that it may be possible to access this kind of information via reflection using Paranamer, but I'd rather not venture into this.
Here is a solution using Dynamic because I wanted to try it out. A macro would decide statically whether to emit an apply of a source value method, the default value method, or just to supply a literal. The syntax could look something like newFrom[C](k). (Update: see below for the macro.)
import scala.language.dynamics
trait Invocable extends Dynamic {
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
def applyDynamic(method: String)(source: Any) = {
require(method endsWith "From")
def caseMethod(s: Symbol) = s.asTerm.isCaseAccessor && s.asTerm.isMethod
val sm = currentMirror reflect source
val ms = sm.symbol.asClass.typeSignature.members filter caseMethod map (_.asMethod)
val values = ms map (m => (m.name, (sm reflectMethod m)()))
val im = currentMirror reflect this
invokeWith(im, method dropRight 4, values.toMap)
}
def invokeWith(im: InstanceMirror, name: String, values: Map[Name, Any]): Any = {
val at = TermName(name)
val ts = im.symbol.typeSignature
val method = (ts member at).asMethod
// supplied value or defarg or default val for type of p
def valueFor(p: Symbol, i: Int): Any = {
if (values contains p.name) values(p.name)
else ts member TermName(s"$name$$default$$${i+1}") match {
case NoSymbol =>
if (p.typeSignature.typeSymbol.asClass.isPrimitive) {
if (p.typeSignature <:< typeOf[Int]) 0
else if (p.typeSignature <:< typeOf[Double]) 0.0
else ???
} else null
case defarg => (im reflectMethod defarg.asMethod)()
}
}
val args = (for (ps <- method.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
(im reflectMethod method)(args: _*)
}
}
case class C(a: String, b: Int, c: Double = 2.0, d: Double)
case class K(b: Int, e: String, a: String)
object C extends Invocable
object Test extends App {
val res = C applyFrom K(8, "oh", "kay")
Console println res // C(kay,8,2.0,0.0)
}
Update: Here is the macro version, more for fun than for profit:
import scala.language.experimental.macros
import scala.reflect.macros._
import scala.collection.mutable.ListBuffer
def newFrom[A, B](source: A): B = macro newFrom_[A, B]
def newFrom_[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(source: c.Expr[A]): c.Expr[B] = {
import c.{ literal, literalNull }
import c.universe._
import treeBuild._
import nme.{ CONSTRUCTOR => Ctor }
def caseMethod(s: Symbol) = s.asTerm.isCaseAccessor && s.asTerm.isMethod
def defaulter(name: Name, i: Int): String = s"${name.encoded}$$default$$${i+1}"
val noargs = List[c.Tree]()
// side effects: first evaluate the arg
val side = ListBuffer[c.Tree]()
val src = TermName(c freshName "src$")
side += ValDef(Modifiers(), src, TypeTree(source.tree.tpe), source.tree)
// take the arg as instance of a case class and use the case members
val a = implicitly[c.WeakTypeTag[A]].tpe
val srcs = (a.members filter caseMethod map (m => (m.name, m.asMethod))).toMap
// construct the target, using src fields, defaults (from the companion), or zero
val b = implicitly[c.WeakTypeTag[B]].tpe
val bm = b.typeSymbol.asClass.companionSymbol.asModule
val bc = bm.moduleClass.asClass.typeSignature
val ps = (b declaration Ctor).asMethod.paramss.flatten.zipWithIndex
val args: List[c.Tree] = ps map { case (p, i) =>
if (srcs contains p.name)
Select(Ident(src), p.name)
else bc member TermName(defaulter(Ctor, i)) match {
case NoSymbol =>
if (p.typeSignature.typeSymbol.asClass.isPrimitive) {
if (p.typeSignature <:< typeOf[Int]) literal(0).tree
else if (p.typeSignature <:< typeOf[Double]) literal(0.0).tree
else ???
} else literalNull.tree
case defarg => Select(mkAttributedRef(bm), defarg.name)
}
}
c.Expr(Block(side.toList, Apply(Select(New(mkAttributedIdent(b.typeSymbol)), Ctor), args)))
}
With usage:
case class C(a: String, b: Int, c: Double = 2.0, d: Double)
case class K(b: Int, e: String, a: String) { def i() = b }
val res = newFrom[K, C](K(8, "oh", "kay"))