Scala Collections: Part 3 - Exploring more operations

In the final post of this series, we're going to continue to look at the variety of operations in the Scala Collection API.

Let’s start with a few operations that builds on ideas we've already looked at.


def takeWhile(p: (A) ⇒ Boolean): Traversable[A]  

Takes the longest prefix of elements that satisfy a predicate.

This operation takes the ideas from the take operation and gives it more flexibility. Instead of having a static number of elements to return, we can now give a predicate to determine when to stop fetching elements.


// Given an infinite recursive method creating a stream of even numbers.
 def even: Stream[Int] = {
     def even0(n: Int): Stream[Int] =
         if (n%2 == 0) n #:: even0(n+1)
         else even0(n+1)

// Return a list of all the elements until an element isn't less then 5.
even takeWhile (x => x < 5) toList

// Output: List(2, 4)


def dropWhile(p: (A) ⇒ Boolean): Traversable[A]  

Drops the longest prefix of elements that satisfy a predicate.

We see the same changes to dropWhile as with takeWhile. It uses a predicate to determine how many elements to drop.


List (1,3,6,5,4,2) dropWhile (x => x < 4)

// Output: List(6, 5, 4, 2)


def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): Traversable[B]  

Takes a function that works on the nested lists and then concatenates the results back together.

flatMap combines the concepts of mapping and flattening into one operation.

List(List(1, 2), List(3, 4)) flatMap(x => => x * x))

// Output: List(1, 4, 9, 16)

flatMap can give some rather surprising results, so make sure to read A collection of Scala flatMap examples by Alvin Alexander for more examples and ideas!

Fold, FoldLeft, FoldRight

These three operations are pretty similar. They all take a start value and a function. The function takes two arguments. The first one is the accumulated value and the second is the value of the current element. Starting with the start value, they iterate the elements of the collection using the function given as an argument to fold the collection.

List(1,2,3,4,5).fold(0)(_ + _)  
List(1,2,3,4,5).foldLeft(0)(_ + _)  
List(1,2,3,4,5).foldRight(0)(_ + _)

// All will output: 15

However, there are differences between the operations.

The main one is that foldLeft iterates the list from left to right, while foldRight iterates the list from right to left. fold however, doesn't guarantee the order of function executions.

Let's create a final example, where we use foldLeft to solve a problem we looked at in the previous post.

// Given a list of tuples (language, released).
// Get all languages created after 2000.
List(("Clojure", 2007), ("Haskell", 1990), ("Scala", 2003)).foldLeft(List[String]())((x, y) => if(y._2 >= 2000) y._1 :: x else x)  


def partition(p: (A) ⇒ Boolean): (Traversable[A], Traversable[A])  

Partitions this traversable collection in two traversable collections according to a predicate.

As you may imagine, this can be very useful sometimes. Let's look at an example where we want to split a list into two lists where one contains the even numbers and the other one the odd numbers.

List(1,2,3,4) partition (x => x % 2 == 0)

// Output: (List(2, 4),List(1, 3))


def zip[B](that: GenIterable[B]): Seq[(A, B)]  

Returns a sequence formed from this sequence and another iterable collection by combining corresponding elements in pairs.

Let's just go straight to the example to see what this means.

List(1,2) zip (List(3,4))

// Outputs: List((1,3), (2,4))

As you can see, it returned a list of tuples where each tuple contains the n-th element from each of the lists.

Now let's look at an operation that can revert this result.


def unzip[A1, A2](implicit asPair: (A) ⇒ (A1, A2)): (Seq[A1], Seq[A2])  

Converts this collection of pairs into two collections of the first and second half of each pair.

As an example, let's unzip the zipped collection from our previous example.

List(1,2) zip (List(3,4)) unzip

// Output: (List(1, 2),List(3, 4))

Great! The unzip operation gave us a tuple back containing the two original lists.

Thats it for now

As you can see, the Scala Collections offer a great range of possibilities. If this brief introduction gave you a taste for more, make sure to checkout the Scala Collection API documentation.

Parts in this series: