Skip to Content
Course content

24: Map and Set Operations

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

I've seen this specific bug more times than I can count when developers move from Java or Python to Scala. It usually happens when they start working with Maps and forget that, by default, Scala's collections are immutable. Check out this snippet—it looks perfectly reasonable at first glance.

case class User(id: Int, name: String)
val sessionCache = Map(1 -> "Active", 2 -> "Idle")

def updateSession(userId: Int, status: String): Unit = {
  // We want to update the status for the user
  sessionCache + (userId -> status) 
  println(s"Updated $userId to $status")
}

updateSession(1, "Idle")
println(sessionCache) // Output: Map(1 -> Active, 2 -> Idle)

The Vanishing Update

You'll notice that the updateSession function runs without an error, but the sessionCache remains exactly as it was. The developer likely thought + modified the map in place. In Scala, Map + (key -> value) doesn't change the existing map; it creates a brand new map containing all the old elements plus the new one.

Because the return value of sessionCache + (userId -> status) was ignored in the function, the new map was simply dropped on the floor and garbage collected. This is a classic "silent failure" that can drive you crazy during debugging.

Returning a New State

The "Scala way" to fix this isn't to switch to a mutable map (though those exist), but to embrace the immutability. We should return the new map from the function and let the caller handle the state. I generally prefer this because it makes the code much easier to test—you don't have to worry about some hidden global state changing under your feet.

case class User(id: Int, name: String)
val sessionCache = Map(1 -> "Active", 2 -> "Idle")

def updateSession(cache: Map[Int, String], userId: Int, status: String): Map[Int, String] = {
  cache + (userId -> status)
}

val updatedCache = updateSession(sessionCache, 1, "Idle")
println(updatedCache) // Output: Map(1 -> Idle, 2 -> Idle)

Comparing Sets with Algebraic Operations

Sets are more than just "lists without duplicates." The real power comes from the set operations that allow you to compare groups of data. If you're dealing with permissions, tags, or unique IDs, these three operations will be your best friends:

  • Intersection (intersect): Finds elements present in both sets. Great for "Which users are in both the Admin group AND the Beta-Tester group?"
  • Union (union): Combines two sets, removing duplicates. Useful for merging two different lists of required dependencies.
  • Difference (diff): Finds elements in the first set that aren't in the second. I use this all the time to find "missing" items, like "Which required fields are missing from this API request?"
val requiredPermissions = Set("READ", "WRITE", "EXECUTE")
val userPermissions = Set("READ", "EXECUTE")

val missing = requiredPermissions.diff(userPermissions) 
// Result: Set("WRITE")

val common = requiredPermissions.intersect(userPermissions)
// Result: Set("READ", "EXECUTE")

Advanced Map Transformations

Once you're comfortable with + and -, you should start looking at how to transform the data inside your maps. Most people know how to use filter, but mapValues (or view.mapValues in Scala 2.13+) is where the real efficiency lies when you only need to touch the data and not the keys.

Imagine you have a map of product IDs to prices, and you need to apply a 10% discount to everything. You don't want to rebuild the whole map structure manually; you just want to transform the values.

val prices = Map("Laptop" -> 1000.0, "Mouse" -> 50.0, "Keyboard" -> 80.0)

// We transform only the values, keeping the keys intact
val discountedPrices = prices.view.mapValues(_ * 0.9).toMap

println(discountedPrices) // Map(Laptop -> 900.0, Mouse -> 45.0, Keyboard -> 72.0)

One last tip: always use getOrElse when retrieving from a Map. Using map(key) will throw a NoSuchElementException if the key is missing, which is a great way to crash your production server at 3 AM. getOrElse lets you define a sensible default or a fallback value.




📋 Practical Task

Building a Role-Based Access Control (RBAC) Validator

Your task is to implement a small permission validator. You will be given a Map where keys are Usernames (Strings) and values are Sets of Permissions (Strings). You need to write a function that checks if a user has all the required permissions for a specific action.

Requirements:

  • Create a Map named userPermissions with at least three users. Each user should have a Set of permissions (e.g., "READ", "WRITE", "DELETE", "ADMIN").
  • Implement a function hasAccess(user: String, required: Set[String]) that:
    • Returns false if the user does not exist in the map.
    • Returns true if the user's permission set contains all the elements in the required set (Hint: use subsetOf or diff).
    • Returns false otherwise.
  • Test your function with a user who has all permissions, a user who only has some, and a user who doesn't exist in the system.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.