Scala
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Scala
-
Section 4: Functional Scala
-
Section 5: Collections in Depth
-
Section 6: Type System
-
Section 7: Concurrency and Ecosystem
-
Section 8: Practical Projects
-
Section 9: Interview Practice
-
Section 10: Data Structures and Algorithms in Scala
-
Section 11: More Practice Exercises
-
Section 12: Advanced Functional Patterns
-
Section 13: More Ecosystem
-
Section 14: Scala Collections Library Deep Dive
-
Section 15: Scala Standard Library Deep Dive
-
Section 16: Akka Ecosystem Deep Dive
-
Section 17: Cats and Cats Effect Deep Dive
-
Section 18: Play Framework Deep Dive
-
Section 19: Apache Spark with Scala Deep Dive
-
Section 20: Scala Build Tools Deep Dive
-
Section 21: Scala 3 Specific Features
-
90: Union and Intersection Types
-
Section 22: Scala Testing Deep Dive
-
Section 23: Functional Domain Modeling
-
Section 24: More Data Structures and Algorithms in Scala
-
Section 25: Scala for Data Engineering
-
Section 26: More Practical Projects
-
Section 27: More Interview and Review
-
Section 28: ZIO Ecosystem Deep Dive
-
Section 29: Scala for Machine Learning
-
Section 30: Scala Microservices Architecture
-
Section 31: Scala Type System Deep Dive
-
Section 32: More Practice and Drills
-
Section 33: Scala Performance Deep Dive
-
Section 34: Scala Ecosystem Tooling
-
Section 35: Scala for Reactive Systems
-
Section 36: More Real-World Case Studies
-
Section 37: Scala for Financial Systems
-
Section 38: Scala GraphQL and gRPC
-
Section 39: More Final Projects
-
Section 40: More Interview and Final Review
-
Section 41: Scala for Streaming Data
-
Section 42: Scala Security Practices
-
Section 43: More Language Deep Dive
-
Section 44: Scala Command-Line Tools
-
Section 45: Scala Documentation and Style
-
Section 46: Scala Dependency Management
-
Section 47: More Practical Backend Patterns
-
Section 48: Scala for Event-Driven Architecture
-
Section 49: More Practice Drills Round 2
-
Section 50: Scala Compiler Deep Dive
-
Section 51: Scala for Web Frontends
-
Section 52: More Data Engineering Practice
-
Section 53: Scala Observability
-
Section 54: More Advanced Practice Projects
-
Section 55: Scala for Legacy Java Integration
-
Section 56: More Testing Practice
-
Section 57: Final Mastery Review
-
Section 58: Scala History and Ecosystem Context
-
Section 59: More Concurrency Patterns
-
Section 60: Scala for Configuration Management
-
Section 61: More Domain Modeling Practice
166: Mock Coding Interview Walkthrough in Scala
Alright, let's step into a mock interview scenario. Imagine the interviewer just handed you this prompt: "Given an array of strings, group the anagrams together."
Usually, when I'm in the hot seat, my first instinct is to get something working—anything—before I start polishing it into "idiomatic Scala." Let's start by sketching out the most basic way to solve this: iterate through the list, sort each word to create a key, and shove them into a map.
The "Just Make It Work" Phase
def groupAnagrams(strs: Array[String]): List[List[String]] = {
val groups = scala.collection.mutable.Map[String, List[String]]()
for (s <- strs) {
val sortedS = s.sorted
if (groups.contains(sortedS)) {
groups(sortedS) = groups(sortedS) :+ s
} else {
groups(sortedS) = List(s)
}
}
groups.values.toList
}
I'll be honest: if I wrote this in a real interview, a senior Scala dev would probably wince. It's too imperative. I'm using a mutable map, a for-loop, and—the real sin here—I'm using :+ to append to a List. Since Scala lists are linked lists, appending to the end is an $O(n)$ operation. If we have a huge group of anagrams, this is going to crawl.
Leaning Into the Functional Toolset
I want to get rid of that mutable map and the manual loop. This is exactly what groupBy was built for. If I can define a "key" for each element, Scala can handle the grouping logic internally. Let's see if we can condense this entire block into essentially one expression.
def groupAnagrams(strs: Array[String]): List[List[String]] = {
strs.groupBy(_.sorted).values.toList.map(_.toList)
}
Wait, that's a lot cleaner. I'm taking the array, grouping it by the sorted version of each string, and then grabbing the values. I added .map(_.toList) at the end because groupBy returns values as Seq, and the prompt usually asks for a specific List return type. It's concise, but we should think about the performance. Sorting every single string takes $O(K \log K)$ where $K$ is the length of the longest string. For most interview cases, that's fine, but is there a way to avoid the sort?
Optimizing the Key Generation
If the strings are long, sorting is the bottleneck. I could instead create a frequency map of the characters (a "count" of each letter a-z) and use that as the key. Let's try to implement a helper function to create this character count string.
def getCharCount(s: String): String = {
val counts = new Array[Int](26)
for (c <- s) {
counts(c - 'a') += 1
}
// Turn the array into a string like "1#0#2#..." to use as a map key
counts.mkString("#")
}
def groupAnagramsOptimized(strs: Array[String]): List[List[String]] = {
strs.groupBy(getCharCount).values.toList.map(_.toList)
}
Now we've shifted the complexity of the key generation from $O(K \log K)$ to $O(K)$. In a real interview, this is the moment where I'd stop and explain the trade-off: the sorted approach is more readable and shorter to write, while the character count approach is more performant for very long strings.
Final Polish for the Interviewer
To wrap this up, I'd make the method more generic. Why limit it to Array[String]? Let's make it take any Seq[String]. Also, I'll use a Map instead of a String for the key in the optimized version if I want to be strictly "type-safe," though a stringified array is often faster in practice.
The final version I'd present looks like this:
def groupAnagrams(strs: Seq[String]): List[List[String]] = {
strs.groupBy { s =>
s.sorted
}.values.map(_.toList).toList
}
I've decided to stick with .sorted for the final "submitted" version because, unless the input strings are massive, the readability win is huge. In a coding interview, clarity beats micro-optimization unless the interviewer specifically asks for it.
📋 Practical Task
Implement a Frequency-Based Grouping for IP Addresses
In this exercise, you'll apply the groupBy pattern we just explored. You are given a list of IP addresses (as strings). You need to group these IP addresses by the number of unique octets they contain.
For example:
"192.168.1.1"has octets [192, 168, 1, 1]. The unique octets are {192, 168, 1}. The count is 3."10.0.0.10"has octets [10, 0, 0, 10]. The unique octets are {10, 0}. The count is 2."127.0.0.1"has octets [127, 0, 0, 1]. The unique octets are {127, 0, 1}. The count is 3.
List("10.0.0.10") and key 3 contains List("192.168.1.1", "127.0.0.1").
Requirements:
- Write a function
groupIPsByUniqueOctets(ips: Seq[String]): Map[Int, List[String]]. - Use the
groupBymethod. - Inside the
groupByblock, split the string by the dot (.), convert the resulting segments to aSetto find the unique values, and return the size of that set.
There are no comments for now.