Scala collections classes: Methods, organized by category

(Note: I’m currently in the process of editing this page, so there may be a few errors in the examples and formatting while I do this.)

When I wrote the Scala Cookbook, I gave each recipe my full attention. I thought that if I wrote each recipe as well as possible, and included important recipes in each chapter, well, I wanted each chapter to be worth the price of the entire book. That was my goal.

As a result of this effort — and perhaps to the chagrin of my editor — the Scala collections chapters ended up being 130 pages in length.

One of the things I tried to do in Chapters 10 and 11 is to make sense and help organize the presentation of the collections classes and methods. For instance, in one recipe on choosing a collection class, I included information like the following table, which is a subset of a larger table:

Choosing a Scala collection class

In another table I show examples of how to call each collection method. The next image shows the beginning of a table that demonstrates the methods that are available on all Traversable collection classes:

Examples of how to use the Scala collections methods

In those examples I use c to stand for collection, f to stand for a function, and p to stand for a predicate.

Categories

In another effort to help make sense of the collections methods, I broke them up into different categories of use. The following lists (and examples) show how I broke up those categories.

We’ll get to those examples in just a moment, but to show those examples we’ll first need some sample data to work with:

Sample Data
-----------

val evens = List(2, 4, 6)
val odds = List(1, 3, 5)

val a = "foo bar baz"
val foo = "foo"
val bar = "bar"
val names = List("Al", "Christina", "Kim")
val firstTen = (1 to 10).toList
val fiveToFifteen = (5 to 15).toList

Given that data, we can now look at the method categories along with examples of each method.

1) Filtering methods

I think of filtering methods as being those methods that filter the original collection into a subset of the original collection, without changing any of the data during the filtering process. So, a filtering method will create a subset of the original collection, without changing any of the elements with an algorithm (such as multiplying each element by two, for example). Given that definition, here is a list of filtering methods available to Scala sequential collection classes:

  • distinct
  • drop
  • dropRight
  • dropWhile
  • filter
  • filterNot
  • find
  • head
  • headOption
  • init
  • last
  • lastOption
  • slice
  • tail
  • take
  • takeRight
  • takeWhile
  • toSet
  • withFilter

Here are examples of each of these methods:

a.distinct                # "fo barz"
a.drop(4)                 # "bar baz"
a.dropRight(2)            # "foo bar b"
a.dropWhile(_ != ' ')     # " bar baz"

a.filter(_ != 'a')        # "foo br bz"
a.filterNot(_ != 'a')     # "aa"
a.filterNot(_ == 'a')     # "foo br bz"
firstTen.find(_ > 4)      # Some(5)

a.head                    # f
a.headOption              # Some(f)

"foo bar".init            # "foo ba"

List(1,2,3).last          # 3
List(1,2,3).lastOption    # Some(3)

a.slice(0,5)              # foo b
a.slice(2,9)              # o bar b

a.tail                    # oo bar baz
a.take(3)                 # foo
a.takeRight(3)            # baz
a.takeWhile(_ != 'r')     # foo ba
Seq(1,1,2,2,3,3).toSet    # Set(1, 2, 3)

firstTen.withFilter(_ > 5)              # scala.collection.generic.FilterMonadic[Int,List[Int]]
firstTen.withFilter(_ > 5).map(_ * 1)   # List[Int] = List(6, 7, 8, 9, 10) 

2) Transformer and reduction methods

Transformer methods take at least one input collection and apply a custom algorithm to that input collection to create a new output collection. Some of these methods can also reduce the original sequential collection down to one (or a few) derived values. These methods include:

  • +
  • ++
  • −−
  • collect
  • diff
  • distinct
  • flatMap
  • map
  • reverse
  • sortWith
  • takeWhile
  • zip
  • zipWithIndex

Here are examples of these collections methods:

evens ++ odds          # List(2, 4, 6, 1, 3, 5)
evens ++: odds         # List(2, 4, 6, 1, 3, 5)
0 +: evens             # List(0, 2, 4, 6)
odds :+ 7              # List(1, 3, 5, 7)
0 :: evens             # List(0, 2, 4, 6)
evens :: odds          # List(List(2, 4, 6), 1, 3, 5)
evens ::: odds         # List(2, 4, 6, 1, 3, 5)
(0 /: evens)(_ + _)    # 12
(evens :\ 0)(_ + _)    # 12

# collect and collectFirst take a partial function
firstTen.collect{case x if x % 2 == 0 => x}   # List(2, 4, 6, 8, 10)
firstTen.collectFirst{case x if x > 1 => x}   # Some(2)

a.diff("foo")                      # " bar baz"
a.distinct                         # "fo barz"

names.flatten                      # List(A, l, C, h, r, i, s, t, i, n, a, K, i, m)
names.flatMap(_.toUpperCase)       # List(A, L, C, H, R, I, S, T, I, N, A, K, I, M)

firstTen.groupBy(_ > 5)            # Map(false -> List(1, 2, 3, 4, 5), true -> List(6, 7, 8, 9, 10))
firstTen.grouped(2)                # Iterator[List[Int]] = non-empty iterator
firstTen.grouped(2).toList         # List(List(1, 2), List(3, 4), List(5, 6), List(7, 8), List(9, 10))
firstTen.grouped(5).toList         # List(List(1, 2, 3, 4, 5), List(6, 7, 8, 9, 10))

firstTen.indices                   # Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
"foo".indices                      # Range(0, 1, 2)

firstTen.intersect(fiveToFifteen)                  # List(5, 6, 7, 8, 9, 10)

a.map(_.toUpper)                   # FOO BAR BAZ
a.map(_.byteValue)                 # Vector(102, 111, 111, 32, 98, 97, 114, 32, 98, 97, 122)
a.mkString(",")                    # f,o,o, ,b,a,r, ,b,a,z
a.mkString("->", ",", "<-")        # ->f,o,o, ,b,a,r, ,b,a,z<-

a.par                              # a parallel array, ParArray(f, o, o,  , b, a, r,  , b, a, z)
a.partition(_ > 'e')               # (foorz, " ba ba")  # a Tuple2
firstTen.partition(_ > 5)          # (List(6, 7, 8, 9, 10),List(1, 2, 3, 4, 5))

a.replace('o', 'x')                # fxx bar baz
a.replace("o", "x")                # fxx bar baz
a.replaceAll("o", "x")             # fxx bar baz
a.replaceFirst("o", "x")           # fxo bar baz
firstTen.reverse                   # List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

# (more on scan, scanLeft, and scanRight below)
Seq(1,2,3,4,5).scan(1)(_ + _)         # List(1, 2, 4, 7, 11, 16)
Seq(1,2,3,4,5).scanLeft(1)(_ + _)     # List(1, 2, 4, 7, 11, 16)
Seq(1,2,3,4,5).scanRight(1)(_ + _)    # List(16, 15, 13, 10, 6, 1)

a.slice(0,5)                       # foo b
a.slice(2,9)                       # o bar b

firstTen.sliding(2)                # Iterator[List[Int]] = non-empty iterator
firstTen.sliding(2).toList         # List(List(1, 2), List(2, 3), List(3, 4), List(4, 5), List(5, 6), List(6, 7), List(7, 8), List(8, 9), List(9, 10))
firstTen.sliding(4).toList         # List(List(1, 2, 3, 4), List(2, 3, 4, 5), List(3, 4, 5, 6), List(4, 5, 6, 7), List(5, 6, 7, 8), List(6, 7, 8, 9), List(7, 8, 9, 10))
firstTen.sliding(2,2).toList       # List(List(1, 2), List(3, 4), List(5, 6), List(7, 8), List(9, 10))
firstTen.sliding(2,3).toList       # List(List(1, 2), List(4, 5), List(7, 8), List(10))
firstTen.sliding(2,4).toList       # List(List(1, 2), List(5, 6), List(9, 10))

a.sortBy                           # this is a bit long; see below
a.sortWith(_ < _)                  # "  aabbfoorz"
a.sortWith(_ > _)                  # "zroofbbaa  "
a.sorted                           # "  aabbfoorz"

firstTen.span(_ < 5)               # (List(1, 2, 3, 4),List(5, 6, 7, 8, 9, 10))
a.split(" ")                       # Array(foo, bar, baz)
a.splitAt(3)                       # (foo," bar baz")
firstTen.startsWith(Seq(1,2))      # true

a.take(3)                          # foo
a.takeRight(3)                     # baz
a.takeWhile(_ != 'r')              # foo ba
a.toArray                          # Array(f, o, o,  , b, a, r,  , b, a, z)
a.toBuffer                         # ArrayBuffer(f, o, o,  , b, a, r,  , b, a, z)
a.toList                           # List(f, o, o,  , b, a, r,  , b, a, z)
Seq(1,1,2,2,3,3).toSet             # Set(1, 2, 3)
firstTen.toStream                  # scala.collection.immutable.Stream[Int] = Stream(1, ?)
a.toLowerCase                      # foo bar baz
a.toUpperCase                      # FOO BAR BAZ
a.toVector                         # Vector(f, o, o,  , b, a, r,  , b, a, z)
a.trim                             # "foo bar baz"

evens.union(odds)                  # List(2, 4, 6, 1, 3, 5)
unzip                              # see below
Seq(1,2,3).updated(0,10)           # List(10, 2, 3)

firstTen.view                      # scala.collection.SeqView[Int,List[Int]] = SeqView(...)

a.zip(0 to 10)                                # Vector((f,10), (o,11), (o,12), ( ,13), (b,14), (a,15), (r,16), ( ,17), (b,18), (a,19), (z,20))
Seq(1,2,3).zipAll(Seq('a', 'b'), 0, 'z')      # List((1,a), (2,b), (3,z))
Seq(1,2).zipAll(Seq('a', 'b', 'c'), 0, 'z')   # List((1,a), (2,b), (0,c))
a.zipWithIndex                                # Vector((f,0), (o,1), (o,2), ( ,3), (b,4), (a,5), (r,6), ( ,7), (b,8), (a,9), (z,10))



3) Sorting methods

As the name implies, sorting methods let you create a new sequence from your initial sequence, where the new sequence has been sorted:

a.sortBy             # this is a bit long; see below
a.sortWith(_ < _)    # "  aabbfoorz"
a.sortWith(_ > _)    # "zroofbbaa  "
a.sorted             # "  aabbfoorz"

sortBy examples

To demonstrate the sortBy method we’ll need some more complicated sample data:

case class Person(firstName: String, lastName: String)

val fred = Person("Fred", "Flintstone")
val wilma = Person("Wilma", "Flintstone")
val barney = Person("Barney", "Rubble")
val betty = Person("Betty", "Rubble")

val people = List(betty, wilma, barney, fred)

Given that data, here’s a sortBy example:

people.sortBy(n => (n.lastName, n.firstName))

Here’s what a couple of examples look like in the Scala REPL:

scala> people.sortBy(n => (n.lastName, n.firstName))
res1: List[Person] = List(Person(Fred,Flintstone), Person(Wilma,Flintstone), Person(Barney,Rubble), Person(Betty,Rubble))

scala> people.sortBy(n => (n.firstName, n.lastName))
res2: List[Person] = List(Person(Barney,Rubble), Person(Betty,Rubble), Person(Fred,Flintstone), Person(Wilma,Flintstone))

4) Grouping methods

These methods let you take an existing collection and create multiple groups from that one input collection. These methods include:

  • groupBy
  • grouped
  • partition
  • sliding
  • span
  • splitAt
  • unzip

Here are examples of these collection methods:

firstTen.groupBy(_ > 5)         # Map(false -> List(1, 2, 3, 4, 5), true -> List(6, 7, 8, 9, 10))
firstTen.grouped(2)             # Iterator[List[Int]] = non-empty iterator
firstTen.grouped(2).toList      # List(List(1, 2), List(3, 4), List(5, 6), List(7, 8), List(9, 10))
firstTen.grouped(5).toList      # List(List(1, 2, 3, 4, 5), List(6, 7, 8, 9, 10))

a.partition(_ > 'e')            # (foorz, " ba ba")  # a Tuple2
firstTen.partition(_ > 5)       # (List(6, 7, 8, 9, 10),List(1, 2, 3, 4, 5))

firstTen.sliding(2)             # Iterator[List[Int]] = non-empty iterator
firstTen.sliding(2).toList      # List(List(1, 2), List(2, 3), List(3, 4), List(4, 5), List(5, 6), List(6, 7), List(7, 8), List(8, 9), List(9, 10))
firstTen.sliding(4).toList      # List(List(1, 2, 3, 4), List(2, 3, 4, 5), List(3, 4, 5, 6), List(4, 5, 6, 7), List(5, 6, 7, 8), List(6, 7, 8, 9), List(7, 8, 9, 10))
firstTen.sliding(2,2).toList    # List(List(1, 2), List(3, 4), List(5, 6), List(7, 8), List(9, 10))
firstTen.sliding(2,3).toList    # List(List(1, 2), List(4, 5), List(7, 8), List(10))
firstTen.sliding(2,4).toList    # List(List(1, 2), List(5, 6), List(9, 10))

firstTen.span(_ < 5)            # (List(1, 2, 3, 4),List(5, 6, 7, 8, 9, 10))
a.split(" ")                    # Array(foo, bar, baz)
a.splitAt(3)                    # (foo," bar baz")

unzip                           # see below

 

5) Informational and mathematical methods

These methods provide information about a collection:

  • canEqual
  • contains
  • containsSlice
  • count
  • endsWith
  • exists
  • find
  • forAll
  • hasDefiniteSize
  • indexOf
  • indexOfSlice
  • indexWhere
  • isDefinedAt
  • isEmpty
  • lastIndexOf
  • lastIndexOfSlice
  • lastIndexWhere
  • max
  • min
  • nonEmpty
  • product
  • segmentLength
  • size
  • startsWith
  • sum

These methods can also be used with a function you supply to obtain information about a collection:

  • foldLeft
  • foldRight
  • reduceLeft
  • reduceRight

Here are examples of these Scala collection informational methods:

a.count(_ == 'a')                  # 2
a.endsWith("baz")                  # true
evens.exists(_ > 2)                # true

firstTen.find(_ > 4)               # Some(5)
firstTen.fold(0)(_ + _)            # 55
firstTen.foldLeft(0)(_ - _)        # 55
firstTen.foldRight(0)(_ - _)       # -5
evens.forall(_ >= 2)               # true

firstTen.hasDefiniteSize           # true
firstTen.toStream.hasDefiniteSize  # false (changes to 'true' after you consume the stream)

a.indexOf('a')                     # 5
firstTen.indexOf(5)                # 4
firstTen.indexOfSlice(Seq(4,5,6))  # 3
firstTen.indexWhere(_ == 5)        # 4
firstTen.indices                   # Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
"foo".indices                      # Range(0, 1, 2)

"foo".isDefinedAt(1)               # true
"foo".isDefinedAt(3)               # false

List(1,2,3).isEmpty                # false
Nil.isEmpty                        # true
None.isEmpty                       # true
Some(1).isEmpty                    # false
a.isEmpty                          # false

a.lastIndexOf('o')                                 # 2
List(1,1,2,2,1,1,3,3).lastIndexOfSlice(Seq(1,1))   # 4
List(1,1,2,2,1,1,3,3).lastIndexWhere(_ == 1)       # 5
a.length                                           # 11

names.max                          # "Kim"
names.min                          # "Al"

a.nonEmpty                         # true

firstTen.reduce(_ + _)             # 55
firstTen.reduceLeft(_ - _)         # -53
firstTen.reduceRight(_ - _)        # Int = -5

# segmentLength
List(1,2,3,4,5,4,3,2,1).segmentLength(_ > 3, 0)   # 0
List(1,2,3,4,5,4,3,2,1).segmentLength(_ > 3, 1)   # 0
List(1,2,3,4,5,4,3,2,1).segmentLength(_ > 3, 2)   # 0
List(1,2,3,4,5,4,3,2,1).segmentLength(_ > 3, 3)   # 3
List(1,2,3,4,5,4,3,2,1).segmentLength(_ > 3, 4)   # 2

a.size                             # 11

firstTen.startsWith(Seq(1,2))      # true

6) Others

A few other methods are more difficult to categorize, including:

  • flatten - converts a list of lists down to one list
  • foreach - like a for loop, letting you iterate over the elements in a collection
  • mkString - lets you build a String from a collection
  • par - creates a parallel collection from an existing collection
  • view - creates a lazy view on a collection (see Recipe 10.24)

Here are examples of some of these Scala sequential collection methods:

foo * 3                            # foofoofoo
a.diff("foo")                      # " bar baz"

names.flatten                      # List(A, l, C, h, r, i, s, t, i, n, a, K, i, m)
names.flatMap(_.toUpperCase)       # List(A, L, C, H, R, I, S, T, I, N, A, K, I, M)

a.foreach(println(_))              # prints one character per line
a.foreach(println)                 # prints one character per line
a.getBytes.foreach(println)        # prints the byte value of each character, one value per line

firstTen.intersect(fiveToFifteen)  # List(5, 6, 7, 8, 9, 10)

a.mkString(",")                    # f,o,o, ,b,a,r, ,b,a,z
a.mkString("->", ",", "<-")        # ->f,o,o, ,b,a,r, ,b,a,z<-

a.par                              # a parallel array, ParArray(f, o, o,  , b, a, r,  , b, a, z)

a.replace('o', 'x')                # fxx bar baz
a.replace("o", "x")                # fxx bar baz
a.replaceAll("o", "x")             # fxx bar baz
a.replaceFirst("o", "x")           # fxo bar baz
firstTen.reverse                   # List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

a.sortBy                           # this is a bit long; see below
a.sortWith(_ < _)                  # "  aabbfoorz"
a.sortWith(_ > _)                  # "zroofbbaa  "
a.sorted                           # "  aabbfoorz"

evens.union(odds)                  # List(2, 4, 6, 1, 3, 5)
Seq(1,2,3).updated(0,10)           # List(10, 2, 3)

firstTen.view                      # scala.collection.SeqView[Int,List[Int]] = SeqView(...)

firstTen.withFilter(_ > 5)                    # scala.collection.generic.FilterMonadic[Int,List[Int]]
firstTen.withFilter(_ > 5).map(_ * 1)         # List[Int] = List(6, 7, 8, 9, 10)

a.zip(0 to 10)                                # Vector((f,10), (o,11), (o,12), ( ,13), (b,14), (a,15), (r,16), ( ,17), (b,18), (a,19), (z,20))
Seq(1,2,3).zipAll(Seq('a', 'b'), 0, 'z')      # List((1,a), (2,b), (3,z))
Seq(1,2).zipAll(Seq('a', 'b', 'c'), 0, 'z')   # List((1,a), (2,b), (0,c))
a.zipWithIndex                                # Vector((f,0), (o,1), (o,2), ( ,3), (b,4), (a,5), (r,6), ( ,7), (b,8), (a,9), (z,10))

Summary

After you work with the Scala collections classes and methods for a while, many of these methods will become second nature. But when you’re first starting with Scala -- or need a quick reference on collections classes and methods you don’t use that often -- I hope that Chapters 10 and 11 in the Scala Cookbook are worth the price of the entire book.