Benjamin Andersen
Benjamin Andersen

Reputation: 482

How to use scala case class as function parameter to specify different fields to use?

I have some duplication of code due to having to do some grouping on 3 different fields in a case class and then populate a new case class with those. Since they share a common schema it should be possible for me to do a function that can take the input of the 3 different fields and populate accordingly. However, I am not exactly sure how to do this.

Schemas:

case class Transaction(
  senderBank: Bank,
  receiverBank: Bank,
  intermediaryBank: Bank)

case class Bank(
  name: String,
  country: Option[String],
  countryCode: Option[String])

case class GroupedBank(
  name: String,
  country: Option[String],
  countryCode: Option[String],
  bankType: String)

Current function I tried to do:

def groupedBank(transactionSeq: Seq[Transaction], bankName: Bank, bankTypeString: String): Iterable[Seq[GroupedBank]] = {
 transactionSeq.groupBy(_ => bankName.name).map {
  case (key, transactionSeq) =>
    val bankGroupedSeq = transactionSeq.map(_ => {
      GroupedBank(
        name = bankName.name,
        country = bankName.country,
        countryCode = bankName.countryCode,
        bankType = bankTypeString)
    })
    bankGroupedSeq
  }
}

I need to do the grouping for SenderBank, receiverBank, and intermediaryBank. However, I am not sure how to refer to them correctly in the function parameter bankName. So for SenderBank I would want to do something like Transaction.senderBank, which would point to the correct fields for name, country and so on for senderBank. For receiverBank it should be similar, so Transactions.receiverBank, which then refers to the correct fields for receiverBank and so on. And again for intermediaryBank the same logic. My question is therefore how can I accomplish something like this or is there another way that would be more appropriate?

Upvotes: 1

Views: 830

Answers (1)

Kolmar
Kolmar

Reputation: 14224

You can pass a function to extract the bank with the correct type from a transaction:

def groupedBank(
  transactionSeq: Seq[Transaction], 
  getBank: Transaction => Bank, 
  bankTypeString: String
): Iterable[Seq[GroupedBank]] = {
  transactionSeq.groupBy(getBank(_).name).map {
    case (key, transactionSeq) =>
      transactionSeq.map { transaction =>
        val bank = getBank(transaction)
        GroupedBank(
          name = bank.name,
          country = bank.country,
          countryCode = bank.countryCode,
          bankType = bankTypeString)
      }
  }
}

And then call it like this:

groupedBank(transactionSeq, _.senderBank, "sender")

It could also be a good idea to abstract the bank type concept into a separate trait:

sealed trait BankGroup {
  def name: String
  def getBank(transaction: Transaction): Bank

  def groupBanks(transactionSeq: Seq[Transaction]): Iterable[Seq[GroupedBank]] = {
    transactionSeq.groupBy(getBank(_).name).map {
      case (key, transactionSeq) =>
        transactionSeq.map { transaction =>
          val bank = getBank(transaction)
          GroupedBank(
            name = bank.name,
            country = bank.country,
            countryCode = bank.countryCode,
            bankType = name)
        }
    }
  }
}

object BankGroup {
  object Sender extends BankGroup {
    def name: String = "sender"
    def getBank(transaction: Transaction): Bank = transaction.senderBank
  }

  object Receiver extends BankGroup {
    def name: String = "receiver"
    def getBank(transaction: Transaction): Bank = transaction.receiverBank
  }

  object Intermediary extends BankGroup {
    def name: String = "intermediary"
    def getBank(transaction: Transaction): Bank = transaction.intermediaryBank
  }

  val values: Seq[BankGroup] = Seq(Sender, Receiver, Intermediary)
  def byName(name: String): BankGroup = values.find(_.name == name)
    .getOrElse(sys.error(s"unknown bank type: $name"))
}

And you can call it in one of those ways:

BankGroup.Sender.groupBanks(transactionSeq)
BankGroup.byName("sender").groupBanks(transactionSeq)

Upvotes: 2

Related Questions