Manar Toumi
Manar Toumi

Reputation: 21

Copying a case class and changing a list of fields

Is there a way to copy a case class in scala using a map of fields and their new values?

I tried a way that included reflection so I would like to avoid that

def copyWithMap(fieldNameValue: Map[String, Option[AnyREF]]): T

when applied to a case class this should make a copy and update the fields that need updating. (specified in the map)

Upvotes: 0

Views: 1559

Answers (2)

James Whiteley
James Whiteley

Reputation: 3474

If you know what you have to update before you start, you can do something like this:

scala> case class Person(name: String, age: Int, eyeColour: String)
defined class Person

scala> val p1 = Person("Bill", 24, "blue")
p1: Person = Person(Bill,24,blue)

scala> val p2 = p1.copy(name = "Ben", eyeColour = "brown")
p2: Person = Person(Ben,24,brown)

If you want to make it more generic, maybe something like this would work (setField taken from Duncan McGregor's answer in the linked post and put in implicit class):

implicit class Modify[T](i: T) {
  def modify(m: Map[String, Any]): T = {
    for ((name, value) <- m) setField(name, value)
    i
  }

  def setField(fieldName: String, fieldValue: Any) = {
    i.getClass.getDeclaredFields.find(_.getName == fieldName) match {
      case Some(field) =>
        field.setAccessible(true)
        field.set(i, fieldValue)
      case None =>
        throw new IllegalArgumentException(s"No field named $fieldName")
    }
  }
}

case class Person(name: String, age: Int, eyeColour: String)

val p1 = Person("Bill", 24, "blue")

val p2 = p1.copy().modify(Map("name" -> "Ben", "eyeColour" -> "brown"))
// p2: Person = Person(Ben,24,brown)

p1
// res0: Person = Person(Bill,24,blue)

Upvotes: 1

Tim
Tim

Reputation: 27356

If you want to do this for any generic object then you need some way of mapping the compile-time name of a field to the run-time location of that field in the object. The only built-in mechanism for this is reflection, so you can't do what you want without using reflection (or implementing your own generic reflection mechanism, which seems pointless).

Upvotes: 2

Related Questions