Question

There are two case classes, they are very similar:

case class A(aaa:String, bbb:String, ccc:String, ddd:String)

case class B(aaa:String, bbb:String, ccc:String, ddd:String, eee:String)

Say I have an instance of class A:

val a = A("111","222","333","444")

Is there any way to create an instance of class B based on A quickly?

I now can do it like this(not very well IMO):

val b = B(a.aaa, a.bbb, a.ccc, a.ddd, "some-eee")
Was it helpful?

Solution

This is pretty straightforward with Shapeless 2.0 (you could do it in Shapeless 1.2.4, but there would be some boilerplate):

scala> import shapeless._, syntax.std.product._
import shapeless._
import syntax.std.product._

scala> val b = Generic[B] from a.productElements :+ "some-eee"
b: B = B(111,222,333,444,some-eee)

And then in the opposite direction:

scala> val c = Generic[A] from b.productElements.init
c: A = A(111,222,333,444)

This approach isn't going to make sure that the names are aligned, but if you get the types wrong it'll complain. It's also definitely possible to use this method to make more complex transformations, although it can get a little messy. For example, suppose we want to add an element in the middle of the parameter list:

case class B(aaa: String, bbb: String, eee: String, ccc: String, ddd: String)

We'd need to write something like the following:

val b = Generic[B].from(
  (a.productElements.take[Nat._2] :+ "some-eee") ++
   a.productElements.drop[Nat._2]
)

Still not that terrible.

Your current solution is about as good as it's going to get if you're not able to use something like Shapeless.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top