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))
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.
I have the following problem which I'm not sure how to model elegantly:
Say I have the following classes:
class A(v: String)
class B(v: String, as: Seq[A])
class C(v: String, bs: Seq[B])
Given an instance of C I want to produce a value for each combination of C x B x A. I want to produce a value even if the C instance doesn't have any Bs or if the B instance doesn't have any As.
For simplicity let's say the value I want to produce is a string of the form c.v - b.v - a.v.
In other words - I'm looking for a way to implement the following method:
def produce(c: C): List[String] = ???
Such that:
val c1 = new C(v = "c1", bs = Seq.empty)
produce(c1) // List("c1 - - ")
val c2 = new C(
v = "c1",
bs = Seq(new B(v = "b1", as = Seq.empty), new B(v = "b2", as = Seq.empty))
)
produce(c2) // List("c1 - b1 - ", "c1 - b2 - ")
val c3 = new C(
v = "c1"
bs = Seq(
new B(v = "b1", as = Seq(new A("a1"), new A("a2)))
new B(v = "b2", as = Seq(new A("a3"), new A("a4))),
)
)
produce(c3) // List("c1 - b1 - a1",
// "c1 - b1 - a2",
// "c1 - b2 - a3",
// "c1 - b2 - a4")
I tried achiving this using a for comprehension but since flatMaping over an empty Seq doesn't yield a value I had to turn empty Seqs into non empty ones. The code turned out complex and long.
My question is what is the best way to do this? Is there some data structure which can help make this simpler?
Thanks.
I think this gets at what you're after. I turned your classes into case classes to make the pattern matching easier.
sealed abstract class VSet
case class A(v: String) extends VSet
case class B(v: String, as: Seq[A]) extends VSet
case class C(v: String, bs: Seq[B]) extends VSet
def produce(vset: VSet, prefix: String = ""): Seq[String] = vset match {
case C(v, bs) => if (bs.isEmpty) Seq(s"$v -")
else bs.flatMap(produce(_, s"$v => "))
case B(v, as) => if (as.isEmpty) Seq(s"$prefix$v -")
else as.flatMap(produce(_, s"$prefix$v => "))
case A(v) => Seq(s"$prefix$v")
}
Say we have these nested classes and an instantiation of A:
class A {
case object B
case class C(c: Int)
}
val a1 = new A()
Now I can check that a1.B is an instance of a1.B.type but how can I check that the type of a1.B is an instance of any A#B.type because the compiler won’t accept that syntax.
a1.B.isInstanceOf[a1.B.type]
res: Boolean = true
a1.B.isInstanceOf[A#B.type]
<console>:1: error: ']' expected but '.' found.
a1.B.isInstanceOf[A#B.type]
^
For the case class it seems to work without problems:
a1.C(0).isInstanceOf[a1.C]
res: Boolean = true
a1.C(0).isInstanceOf[A#C]
res: Boolean = true
Follow-up question: When I have
val a1 = new A()
val a2 = new A()
is there a function that does an equality check without taking path-dependency into account? Eg. it should return true when comparing a1.B and a2.B. For example:
a1.B =#= a2.B
true
a1.C(0) =#= a2.C(0)
true
a1.C(0) =#= a2.C(1)
false
Edit: For clarification: Just introducing a common trait for B is not enough as I want to bet able to distinguish between case objects:
class A {
trait BB
case object B1 extends BB
case object B2 extends BB
}
val a1 = new A
val a2 = new A
a1.B1 =#= a2.B1 // should be true
a1.B2 =#= a2.B2 // should be true
a1.B1 =#= a1.B2 // should be false
a1.B1 =#= a2.B2 // should be false
Now, the .hashCode (or .##) method seems to solve the problem:
a1.B1.## == a2.B1.## // true
a1.B1.## == a2.B2.## // false
but maybe there is a more elegant solution (I’d also like to be able to use a1.B1 in a pattern match for example).
Just use full form for expressing existential types.
Here your example:
class A {
case object B1
case object B2
case class C(c: Int)
}
val a1 = new A()
I've added another case object to demonstrate that they could be distinguished and so I have not write an obscure equivalent for AnyRef
type AB1 = a.B1.type forSome {val a : A}
type AB2 = a.B2.type forSome {val a : A}
scala> a1.B1.isInstanceOf[AB1]
res0: Boolean = true
scala> a1.B1.isInstanceOf[AB2]
res1: Boolean = false
I've introduced type aliases AB1 and AB2 for convenience. But that types may be inlined into isInstanceOf if there is a need.
I think a1.B.isInstanceOf[A#B.type] is syntacticly wrong. There is no .type
How about using a inner trait
class A{
trait BTrait
object B extends BTrait
}
val a1 = new A
a1.B.isInstanceOf[A#BTrait] // this is true
if you want your inner instances to be equal independent of the outer instance,
a1.C(0) == a2.C(0)
is ture if you declare C as
final case class C()
this does not work for objects
I am not sure if this is purely a syntax thing but the following seems to solve it:
class A {
case object B
type BType = B.type
case class C(c: Int)
}
val a1 = new A()
val a2 = new A()
a1.B.isInstanceOf[a1.B.type]
res: Boolean = true
a1.B.isInstanceOf[A#BType]
res: Boolean = true
And then I can kill the (2.11) compiler with
a1.B match {
case _: A#BType => println("ABC")
}
> error: scala.MatchError: (?_1.type#B.type,a1.B.type) (of class scala.Tuple2)
In dotty it works though and interestingly enough a1.B.type and a1.BType are different:
List(a1, a2) foreach { a =>
a.B match {
case _: a1.B.type => println("Matches a1")
case _: a2.B.type => println("Matches a2")
case _: A#BType => println("Matches a1 and a2")
case _: a1.BType => println("Matches a1 and a2")
case _: a2.BType => println("Matches a1 and a2")
case x => println(x)
}
}
I'm working on some Scala code where the user will have a variable number of named variables, all of the same type A:
val a1: A = getA1()
val a2: A = getA2()
val a3: A = getA3()
// ...
I would like to define a class called Processor that takes these arguments as parameters, transforms each parameter into a new type B, and exposes a method called process that gives the user access to these new transformed variables. The best API I've come up with so far is:
class Processor(a1: A, a2: A, a3: A) {
private val b1 = toB(a1)
private val b2 = toB(a2)
private val b3 = toB(a3)
def process(f: (B, B, B) => C): C = {
f(b1, b2, b3)
}
}
// Usage:
val a1: A = getA1()
val a2: A = getA2()
val a3: A = getA3()
val p = new Processor(a1, a2, a3)
p.process((b1, b2, b3) => doSomething(b1, b2, b3))
The problem with this API is that it only works for three parameters, whereas the user could have anywhere from 1 to 20 parameters. I could define Processor1, Processor2, Processor3, etc classes that take each take a different number of parameters, but this would be a repetitive and ugly API. Another option is to take A* as a parameter in both the Processor constructor and process method:
class Processor(a: A*) {
private val b = a.map(toB)
def process(f: (Seq[B]) => C): C = {
f(b)
}
}
// Usage:
val a1: A = getA1()
val a2: A = getA2()
val a3: A = getA3()
val p = new Processor(Vector(a1, a2, a3))
p.process((b) => doSomething(b(0), b(1), b(2)))
This API works for any number of parameters, but it no longer has type safety or named parameters, so the code could blow up at runtime if the user accidentally looked up an index that did not exist (e.g. b(3)). In other words, the compiler no longer verifies that the number of parameters passed to the Processor constructor matches the number used in the process method.
Is there any way in Scala to have a dynamic number of named/typed parameters? Perhaps using a macro?
With the help of shapeless, you can define Processor like:
class Processor[T](t: T)
(implicit
mapper: ops.tuple.Mapper.Aux[T, ToB.type]
) {
private val bs = mapper(t)
def process[C](f: mapper.Out => C): C = {
f(bs)
}
}
(shapeless._ was previously imported.)
It can be used like
val p4 = new Processor(a1, a2, a3, a4)
p4.process{case (b1, b2, b3, b4) => ??? }
Here, when Processor is instantiated with a tuple of As with type T, it requires an implicit shapeless.ops.tuple.Mapper, that allows to map toB (wrapped in ToB, see below) on this tuple. The type member Out of this implicit is the "output" tuple, made of Bs. It is used in the signature of process.
ToB is defined like
object ToB extends Poly1 {
implicit def apply = at[A](a => toB(a))
}
and allows to map toB on a tuple with ops.tuple.Mapper.
Simplified Use Case
sealed trait T1
case object Foo extends T1
case object Bar extends T1
sealed trait T2
case object Bip extends T2
case object Bop extends T2
case class Partitioner(v1: T1, v2: T2)
trait WithPartition { self =>
def pt: Partitioner
def byT1(t1: T1): Boolean = self.pt.v1 == t1
def byT2(t2: T2): Boolean = self.pt.v2 == t2
}
case class PSetOne(pt: Partitioner, extraOne: Int) extends WithPartition
case class PSetTwo(pt: Partitioner, extraTwo: Int) extends WithPartition
// create lists
val ls1 = for {
p1 <- List(Foo,Bar)
p2 <- List(Bip,Bop)
n <- 1 to 10
} yield PSetOne(Partitioner(p1,p2),n)
val ls2 = for {
p1 <- List(Foo,Bar)
p2 <- List(Bip,Bop)
n <- 1 to 10
} yield PSetTwo(Partitioner(p1,p2),n)
// So I can filter on the list
ls2.filter{x => x.byT1(Foo)}
// ls1 ++ ls2 => List[Product with Serializable with WithPartition]
// but prefer to retain type using HList
val hAll = ls1 :: ls2 :: HNil
// I can filter if I explicity supply the case object
object parFoo extends Poly1 {
implicit def casePset1 = at[List[PSetOne]](_.filter{x => x.byT1(Foo)})
implicit def casePset2 = at[List[PSetTwo]](_.filter{x => x.byT1(Foo)})
}
hAll.map(parFoo)
What I would like to be able to do is pass the Partition value as a parameter to be able to
do something like
hAll.map(parT1(Foo))
How might I specify object parT1 such that it can use a T1 object as a parameter to the filter asin the vanilla list example above
I investigated the duplicate reference but the technique was beyond me to understand and apply in the above situation. I have found an alternative non shapeless based solution which is to emend the code with some additional type information.
trait PSet
type PSP = PSet with WithPartition
case class PSetOne(pt: Partitioner, extraOne: Int) extends PSet with WithPartition
case class PSetTwo(pt: Partitioner, extraTwo: Int) extends PSet with WithPartition
// create lists with type ascription
val ls1 = for {
p1 <- List(Foo,Bar)
p2 <- List(Bip,Bop)
n <- 1 to 10
} yield PSetOne(Partitioner(p1,p2),n) : PSP
val ls2 = for {
p1 <- List(Foo,Bar)
p2 <- List(Bip,Bop)
n <- 1 to 10
} yield PSetTwo(Partitioner(p1,p2),n) : PSP
val als = ls1 ++ ls2
// now gives me List[PSP] and I can
val foos = als.filter{x => x.byT1(Foo)}
val bips = als.filter{x => x.byT2(Bip)}
// and still extract by case class
val foos1 = foos.collect{case f: PSetOne => f}
I am not altogether satisfied with this (though I do get to use a type alias of PSP :)) but it will do as I need for now.
If anyone can post a shapeless based solution I would be grateful, not least to help me improve my understanding.