сравнить поля класса дел с подполями другого класса дел в Scala - PullRequest
5 голосов
/ 12 мая 2019

У меня есть 3 следующих класса дел:

case class Profile(name: String,
                   age: Int,
                   bankInfoData: BankInfoData,
                   userUpdatedFields: Option[UserUpdatedFields])

case class BankInfoData(accountNumber: Int,
                        bankAddress: String,
                        bankNumber: Int,
                        contactPerson: String,
                        phoneNumber: Int,
                        accountType: AccountType)

case class UserUpdatedFields(contactPerson: String,
                             phoneNumber: Int,
                             accountType: AccountType)

это просто перечисления, но я все равно добавил:

sealed trait AccountType extends EnumEntry

object AccountType extends Enum[AccountType] {
  val values: IndexedSeq[AccountType] = findValues

  case object Personal extends AccountType

  case object Business extends AccountType

}

моя задача - мне нужно написать funcc Profile и сравните UserUpdatedFields (все поля) с НЕКОТОРЫМИ полями в BankInfoData ... эта функция должна найти, какие поля были обновлены.

, поэтому я написал эту функцию:

def findDiff(profile: Profile): Seq[String] = {
  var listOfFieldsThatChanged: List[String] = List.empty
  if (profile.bankInfoData.contactPerson != profile.userUpdatedFields.get.contactPerson){
    listOfFieldsThatChanged = listOfFieldsThatChanged :+ "contactPerson"
  }
  if (profile.bankInfoData.phoneNumber != profile.userUpdatedFields.get.phoneNumber) {
    listOfFieldsThatChanged = listOfFieldsThatChanged :+ "phoneNumber"
  }
  if (profile.bankInfoData.accountType != profile.userUpdatedFields.get.accountType) {
    listOfFieldsThatChanged = listOfFieldsThatChanged :+ "accountType"
  }
  listOfFieldsThatChanged
}

val profile =
  Profile(
    "nir",
    34,
    BankInfoData(1, "somewhere", 2, "john", 123, AccountType.Personal),
    Some(UserUpdatedFields("lee", 321, AccountType.Personal))
  )

findDiff(profile)

работает, но хотел чего-нибудь чище .. есть предложения?

Ответы [ 3 ]

3 голосов
/ 12 мая 2019

Простое улучшение состояло бы в том, чтобы ввести черту

trait Fields {
  val contactPerson: String
  val phoneNumber: Int
  val accountType: AccountType

  def findDiff(that: Fields): Seq[String] = Seq(
    Some(contactPerson).filter(_ != that.contactPerson).map(_ => "contactPerson"),
    Some(phoneNumber).filter(_ != that.phoneNumber).map(_ => "phoneNumber"),
    Some(accountType).filter(_ != that.accountType).map(_ => "accountType")
  ).flatten
}

case class BankInfoData(accountNumber: Int,
                          bankAddress: String,
                          bankNumber: Int,
                          contactPerson: String,
                          phoneNumber: Int,
                          accountType: String) extends Fields

case class UserUpdatedFields(contactPerson: String,
                           phoneNumber: Int,
                           accountType: AccountType) extends Fields

, чтобы можно было позвонить

BankInfoData(...). findDiff(UserUpdatedFields(...))

Если вы хотите еще больше улучшить и не называть все поля множественнымивремена, например бесформенные могут быть использованы для компиляции.Не совсем то же самое, но что-то вроде this , чтобы начать.Или используйте отражение, чтобы сделать это во время выполнения, как этот ответ .

2 голосов
/ 12 мая 2019

Каждый класс case расширяет интерфейс Product , поэтому мы можем использовать его для преобразования классов case в наборы элементов (field, value).Затем мы можем использовать операции над множествами, чтобы найти разницу.Например,

  def findDiff(profile: Profile): Seq[String] = {
    val userUpdatedFields = profile.userUpdatedFields.get
    val bankInfoData = profile.bankInfoData

    val updatedFieldsMap = userUpdatedFields.productElementNames.zip(userUpdatedFields.productIterator).toMap
    val bankInfoDataMap = bankInfoData.productElementNames.zip(bankInfoData.productIterator).toMap
    val bankInfoDataSubsetMap = bankInfoDataMap.view.filterKeys(userUpdatedFieldsMap.keys.toList.contains)
    (bankInfoDataSubsetMap.toSet diff updatedFieldsMap.toSet).toList.map { case (field, value) => field }
  }

Теперь findDiff(profile) должно выдать List(phoneNumber, contactPerson).Обратите внимание, что мы используем productElementNames из Scala 2.13 для получения имен файлов, которые мы затем архивируем с соответствующими значениями

userUpdatedFields.productElementNames.zip(userUpdatedFields.productIterator)

Также мы используем filterKeys и Diff .

1 голос
/ 12 мая 2019

Эту задачу было бы очень легко выполнить, если бы это был простой способ преобразования класса наблюдения в карту.К сожалению, классы case не предлагают такую ​​функциональность из коробки в Scala 2.12 (как уже упоминал Марио, это будет легко реализовать в Scala 2.13).

Есть библиотека с именем shapeless , который предлагает некоторые общие программные утилиты.Например, мы могли бы написать функцию расширения toMap, используя Record и ToMap из бесформенного:

object Mappable {
  implicit class RichCaseClass[X](val x: X) extends AnyVal {
    import shapeless._
    import ops.record._

    def toMap[L <: HList](
        implicit gen: LabelledGeneric.Aux[X, L],
        toMap: ToMap[L]
    ): Map[String, Any] =
      toMap(gen.to(x)).map{
        case (k: Symbol, v) => k.name -> v
      }
    }
}

Тогда мы могли бы использовать ее для findDiff:

def findDiff(profile: Profile): Seq[String] = {
  import Mappable._

  profile match {
    case Profile(_, _, bankInfo, Some(userUpdatedFields)) =>
      val bankInfoMap = bankInfo.toMap
      userUpdatedFields.toMap.toList.flatMap{
        case (k, v) if bankInfoMap.get(k).exists(_ != v) => Some(k)
        case _ => None
      }
    case _ => Seq()
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...