(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:
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:
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 listforeach
- like a for loop, letting you iterate over the elements in a collectionmkString
- lets you build a String from a collectionpar
- creates a parallel collection from an existing collectionview
- 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))
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
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.