Skip to Content
Course content

167: Timed Mock Assessment on Core Scala

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Alright, we've hit lesson 167. This is the "Mock Assessment" phase. Now, usually, a mock assessment is just a list of questions, but that's not how you actually learn to pass these things under pressure. Instead, I want to walk you through how I tackle a complex "core Scala" challenge when the clock is ticking. I've got a prompt here that asks me to build a LoyaltyCalculator that processes a stream of customer transactions and returns a tiered status based on specific business rules.

The goal: calculate a score where returns subtract points, high-value purchases add bonus points, and frequent small purchases provide a consistency multiplier. It sounds simple, but in a timed environment, it's easy to write "Java-in-Scala" rather than idiomatic Scala. Let's dive in.

Getting the basic logic down

First, I'll define my data structures. I need a Transaction case class. I'll start by trying to solve this with a simple filter and sum approach. It's the most intuitive way to start.

case class Transaction(id: String, amount: Double, isReturn: Boolean)

def calculateScore(txs: List[Transaction]): Double = {
  val purchases = txs.filter(!_.isReturn).map(_.amount).sum
  val returns = txs.filter(_.isReturn).map(_.amount).sum
  purchases - returns
}

I ran this against a sample set, and it works, but it's inefficient. I'm traversing the list twice—once for purchases and once for returns. If this were a real-world dataset with millions of transactions, this would be a performance nightmare. In a core Scala assessment, the reviewer isn't just looking for the right answer; they're looking to see if you understand how to traverse a collection efficiently.

The MapValues trap

Now, the prompt adds a twist: the score needs to be calculated per customer from a master list of transactions. My first instinct is to use groupBy. Let's see where that leads me.

case class CustomerTx(customerId: Int, tx: Transaction)

def scoresPerCustomer(data: List[CustomerTx]): Map[Int, Double] = {
  data.groupBy(_.customerId).mapValues { txs => 
    val total = txs.map(_.tx).foldLeft(0.0)((acc, t) => if(t.isReturn) acc - t.amount else acc + t.amount)
    total
  }
}

I hit a snag here. If you're using Scala 2.13+, mapValues returns a MapView, not a strict Map. If I try to pass this result into another function that expects a concrete Map, I'm going to get a type mismatch error that'll waste five minutes of my timed session. I need to call .toMap at the end to materialize the results. It's a small detail, but these are the things that trip people up during assessments.

Tightening the loop for performance

I'm still not happy. The groupBy creates a bunch of intermediate lists. If I really want to show off "core Scala" proficiency, I should use a foldLeft to build the map in a single pass. It's a bit more verbose, but it's the "engineer's way" to do it.

I'll try to accumulate the totals directly into a map. I'll use getOrElse to handle the case where the customer isn't in the map yet.

def optimizedScores(data: List[CustomerTx]): Map[Int, Double] = {
  data.foldLeft(Map.empty[Int, Double]) { (acc, item) =>
    val currentScore = acc.getOrElse(item.customerId, 0.0)
    val change = if (item.tx.isReturn) -item.tx.amount else item.tx.amount
    acc + (item.customerId -> (currentScore + change))
  }
}

This is much cleaner. I'm visiting every element exactly once, and I'm avoiding the overhead of creating intermediate grouped lists. I've moved from a "naive" solution to a "functional" solution, and finally to an "optimized" one. That's the trajectory you want to demonstrate in your assessment: show that you can get it working, then show that you know why the first way was suboptimal, and then provide the professional version.




📋 Practical Task

Exercise: Building a High-Frequency Trade Aggregator

You are tasked with creating a trade aggregator for a financial system. You are given a list of Trade objects, where each trade has a symbol (e.g., "AAPL"), a quantity, and a price. Some trades are 'Buy' and some are 'Sell'.

Requirements:

  • Implement a function calculateNetPosition(trades: List[Trade]).
  • The function must return a Map[String, Double] where the key is the stock symbol and the value is the net quantity held (Buy increases quantity, Sell decreases it).
  • You must implement this using a foldLeft to ensure the collection is traversed only once.
  • Do not use groupBy, as the goal is to optimize for a high-volume stream of data.
case class Trade(symbol: String, quantity: Double, price: Double, side: Side)
sealed trait Side
case object Buy extends Side
case object Sell extends Side

// Your implementation here:
def calculateNetPosition(trades: List[Trade]): Map[String, Double] = {
  ???
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.