2

I have 2 case classes:

case class Person(fname: String, lname: String, age: Int, address: String)

case class PersonUpdate(fname: String, lname: String, age: Int)

so PersonUpdate dosent have all the fields Person have, and I want to write effective that get Person and PersonUpdate and find the fields that have different values:

for example:

def findChangedFields(person: Person, personUpdate: PersonUpdate): Seq[String] = {

  var listOfChangedFields: List[String] = List.empty

  if (person.fname == personUpdate.fname)
    listOfChangedFields = listOfChangedFields :+ "fname"

  if (person.lname == personUpdate.lname)
    listOfChangedFields = listOfChangedFields :+ "lname"
  if (person.age == personUpdate.age)
    listOfChangedFields = listOfChangedFields :+ "age"

  listOfChangedFields
}

findChangedFields(per, perUpdate)

but this is very ugly, how can I write this nicely with the magic of scala?

JohnBigs
  • 2,691
  • 3
  • 31
  • 61

3 Answers3

3

Something like this, maybe?

  val fields = Seq("fname", "lname", "age")
  val changedFields = person.productIterator
   .zip(personUpdate.productIterator) 
   .zip(fields.iterator)
   .collect { case ((a, b), name) if a != b => name }
   .toList
Dima
  • 39,570
  • 6
  • 44
  • 70
  • the problem here is that you are assuming here that person and personUpdate are the same length...person have 4 fields and personUpdate have 3...@Dime – JohnBigs Apr 23 '19 at 16:25
  • @JohnBigs no, I am not assuming that. As long as "extra fields" come at the end, it's ok – Dima Apr 23 '19 at 16:32
  • ok, so they have to be the same order. anyway ill accept it cause it fits the question, hopefully ill be able to have them in the same order, i might :) thanks! – JohnBigs Apr 24 '19 at 08:07
0

Something like this:

case class Person(fname: String, lname: String, age: Int, address: String)

case class PersonUpdate(fname: String, lname: String, age: Int)

def findFirstNameChanged(person: Person, personUpdate: PersonUpdate): List[String] = 
{

  if (person.fname == personUpdate.fname)  List("fname")
  else Nil
}

def findLastNameChanged(person: Person, personUpdate: PersonUpdate): List[String] = {

  if (person.lname == personUpdate.lname)  List("lname")
  else Nil
}

def findAgeNameChanged(person: Person, personUpdate: PersonUpdate): List[String] = {

  if (person.age == personUpdate.age)  List("age")
  else Nil
}

def findChangedFields(person: Person, personUpdate: PersonUpdate): Seq[String] = {

  findFirstNameChanged(person,personUpdate)::: 
findLastNameChanged(person,personUpdate) ::: findAgeNameChanged(person,personUpdate)
}

val per = Person("Pedro","Luis",22,"street")
val personUpdate = PersonUpdate("Pedro", "Luis",27)

findChangedFields(per, personUpdate)
Pedro Correia Luís
  • 1,085
  • 6
  • 16
0

I think your problem is similar to compare two Set of tuple. Please feel free two correct me.

So here is my solution which will work for any two case class having field names in any order

def caseClassToSet[T](inp: T)(implicit ct: ClassTag[T]): Set[(String, AnyRef)] = {
ct.runtimeClass.getDeclaredFields.map(f => {
  f.setAccessible(true)
  val res = (f.getName, f.get(inp))
  f.setAccessible(false)
  res
}).toSet
}

val person = Person("x", "y", 10, "xy")
val personUpdate = PersonUpdate("z","y",12)
val personParams: Set[(String, AnyRef)] = caseClassToSet(person)
val personUpdateParams: Set[(String, AnyRef)] = caseClassToSet(personUpdate)

println(personUpdateParams diff personParams)

Got help from Get field names list from case class