Day 2 of 5
⏱ ~60 minutes
Scala in 5 Days — Day 2

Functional Collections & Higher-Order Functions

Scala's collection library is the most powerful in any language. Immutable by default, it provides map, filter, flatMap, fold, and dozens more operations that transform data without mutation. Today you master the collection API.

Immutable Collections

Scala's default collections are immutable: List (linked list), Vector (indexed, fast random access), Set (unique elements), Map (key-value). All operations return new collections — the original is unchanged. Mutable collections (scala.collection.mutable) exist but are used sparingly. Vector is the general-purpose default: O(1) amortized for append, prepend, and random access via 32-way tree structure.

map, flatMap, and for-comprehensions

map transforms each element. filter keeps matching elements. flatMap maps then flattens — essential for working with Option, Either, and nested collections. For-comprehensions are syntactic sugar for map/flatMap/filter: 'for { x <- xs; y <- ys if x != y } yield (x, y)'. For-comprehensions work on any Monad: List, Option, Future, Either.

fold, reduce, and groupBy

foldLeft(initial)(op) reduces left-to-right with an accumulator. foldRight reduces right-to-left. reduce applies a binary operation without an initial value (requires non-empty collection). groupBy(f) partitions into a Map[K, List[V]]. sortBy, sortWith, zip, unzip, partition, span, take, drop, takeWhile, dropWhile — the full collection API replaces every loop pattern you have written in imperative languages.

scala
// Immutable collection operations
val numbers = (1 to 20).toVector

// Chain transformations
val result = numbers
  .filter(_ % 2 == 0)       // evens: [2,4,6,...,20]
  .map(n => n * n)           // squares: [4,16,36,...,400]
  .takeWhile(_ < 200)        // [4,16,36,64,100,144,196]
  .foldLeft(0)(_ + _)        // sum: 460

println(result)  // 460

// For-comprehension: Pythagorean triples
val triples = for {
  c <- 1 to 100
  b <- 1 to c
  a <- 1 to b
  if a*a + b*b == c*c
} yield (a, b, c)

triples.take(5).foreach(println)
// (3,4,5), (5,12,13), (6,8,10), (8,15,17), (9,12,15)

// groupBy: word frequencies
val words = 'the quick brown fox jumps over the lazy dog the'.split(' ')
val freq = words.groupBy(identity).view.mapValues(_.length).toMap
freq.toList.sortBy(-_._2).foreach { case (w, c) => println(s'$w: $c') }
💡
Use .view before chaining many transformations on a large collection. A view makes the chain lazy — intermediate collections are not created. Call .toList, .toVector, or .force at the end to materialize the result.
📝 Day 2 Exercise
Process Data with Collections
  1. Define a List of case class Student(name: String, grade: Int, score: Double)
  2. Find all students with score > 85 using filter
  3. Compute average score per grade using groupBy + mapValues(mean)
  4. Sort students by score descending and take the top 3
  5. Use a for-comprehension to find all pairs of students in the same grade

Day 2 Summary

  • Scala collections are immutable by default — operations return new collections
  • Vector is the general-purpose default collection for O(1) indexed access
  • flatMap enables chaining operations that return collections or Option
  • For-comprehensions desugar to map/flatMap/filter chains
  • groupBy partitions a collection into a Map keyed by a function's result
Challenge

Implement a word frequency analyzer that reads a text file, tokenizes it, removes stop words, stems (or lemmatizes) remaining words, and produces a sorted frequency table. Use only immutable collections and functional operations — no vars, no loops.

Finished this lesson?