10 Amazing Scala Collection Functions
These 10 collection functions, ranging from min and max values to folding to flatmaps, will come in handy during your day-to-day work.
Join the DZone community and get the full member experience.
Join For FreeWhen I work with Scala collections, I always keep in mind that there are two types of operations that I can perform: transformation operations and actions or aggregation operations. The first type transforms a collection into some another collection. The second one type returns some value.
After this short introduction, I want to focus on particular Scala collection functions, which I count as some of the most useful and amazing parts of my everyday work. Some of these functions are to transform collections, and the rest of them return a particular value after application. And at the end of the article, I want to show you how these functions can be combined in order to solve a concrete problem.
#1 Minimum and Maximum Values
I want to start with the action function.
It’s so common a task to find a minimum or maximum value in a sequence. Of course, you may say that these kind of operations are helpful only for interview questions and algorithms, but let’s be honest, who doesn't remember these lines of code in Java?
int[] arr = {11, 2, 5, 1, 6, 3, 9};
int to = arr.length - 1;
int max = arr[0];
for (int i = 0; i < to; i++)
{ if (max < arr[i+1]) max = arr[i+1]; }
System.out.println(max);
Question: How to find maximum/minimum in a list?
Scala suggests a pretty elegant solution:
val numbers = Seq(11, 2, 5, 1, 6, 3, 9)
numbers.max //11
numbers.min //1
But we always work with more complex data. Let’s introduce a more advanced example, where we have a sequence of books, represented by case classes.
case class Book(title: String, pages: Int)
val books = Seq( Book("Future of Scala developers", 85),
Book("Parallel algorithms", 240),
Book("Object Oriented Programming", 130),
Book("Mobile Development", 495) )
//Book(Mobile Development,495)
books.maxBy(book => book.pages)
//Book(Future of Scala developers,85)
books.minBy(book => book.pages)
So as you see, minBy
& maxBy
functions solve problems with non-trivial data. The only thing you need to do is to choose a data property by which you want to determine minimum or maximum values.
#2 Filtering
Have you ever filtered collections? For example, say you want to get items with a price of more than $10 or you need to select the youngest employees under 24 years. All these operations imply using filtering.
Let’s start with the popular example: filter a list of numbers and get only even elements.
val numbers = Seq(1,2,3,4,5,6,7,8,9,10) numbers.filter(n => n % 2 == 0)
What about a more complex scenario? I want to choose books where the number of pages are more than 120.
val books = Seq( Book("Future of Scala developers", 85),
Book("Parallel algorithms", 240),
Book("Object Oriented Programming", 130),
Book("Mobile Development", 495) )
books.filter(book => book.pages >= 120)
Filtering is not much harder to apply than the min
& max
functions, despite the fact that filter
is a function of the transformation type.
Also, there is a syntax sugar analog of the filter
function. Its name is filterNot
. I guess, you know what it does by its name. If not, try to substitute the filter
function for filterNot
in the first example.
#3 Flatten O_o
I bet, there is a huge chance that you haven’t heard about this function before! It’s easy to explain. Because its application is extremely specific. For me, it’s hard to describe this function without an example.
val abcd = Seq('a', 'b', 'c', 'd')
val efgj = Seq('e', 'f', 'g', 'h')
val ijkl = Seq('i', 'j', 'k', 'l')
val mnop = Seq('m', 'n', 'o', 'p')
val qrst = Seq('q', 'r', 's', 't')
val uvwx = Seq('u', 'v', 'w', 'x')
val yz = Seq('y', 'z')
val alphabet = Seq(abcd, efgj, ijkl, mnop, qrst, uvwx, yz)
//
// List(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t,
// u, v, w, x, y, z)
alphabet.flatten
When is flatten
helpful? Well, if you have a collection of collections and you want to operate with all of the elements from the collections, don’t hesitate to use flatten
.
#4 Euler Diagram Functions
Don’t panic! I talk about a well-known operation: difference, intersection, union. I hope, you agree with me that these functions are good to be explained on Euler diagrams:
val num1 = Seq(1, 2, 3, 4, 5, 6)
val num2 = Seq(4, 5, 6, 7, 8, 9)
//List(1, 2, 3)
num1.diff(num2)
//List(4, 5, 6)
num1.intersect(num2)
//List(1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9)
num1.union(num2)
The examples are self-explanatory. But what about the union
function? It keeps duplicates. What if we want to get rid of them? For this purpose, use the distinct
function:
//List(1, 2, 3, 4, 5, 6, 7, 8, 9)
num1.union(num2).distinct
Here is an illustration of the functions above:
#5 map
List Elements
Probably map
is the most widely used function in Scala collections. Its power is better to show off than talk about it:
val numbers = Seq(1,2,3,4,5,6)
//List(2, 4, 6, 8, 10, 12)
numbers.map(n => n * 2)
val chars = Seq('a', 'b', 'c', 'd')
//List(A, B, C, D)
chars.map(ch => ch.toUpper)
The logic of the map
function is that you iterate through each element of a collection and apply a function to the elements. You can even leave elements as they are, without applying any function, but in this case, the map
function is absolutely useless because you will get the same collection after mapping.
#6 flatMap
I still remember how was difficult for me to understand where and how to apply the flatMap
function. In general, this is caused by wide variety of situations where flatMap
can be helpful. The first thing I recommend is for every beginner to look at the name of the function more attentively. You should notice that flatMap
consists of two functions that we’ve already considered above:
map
& flatten
Let’s assume that we want to see how uppercase and lowercase characters look in the alphabet.
val abcd = Seq('a', 'b', 'c', 'd')
//List(A, a, B, b, C, c, D, d)
abcd.flatMap(ch => List(ch.toUpper, ch))
Since in this article I talk only about collection functions, I’ll omit examples with Future
s and Option
s.
#7 Check the Entire Collection for a Condition
There is a well-known scenario when you need to ensure that all elements in a collection meet some requirement. If at least one of the elements doesn’t correspond to the condition, you need to do something.
val numbers = Seq(3, 7, 2, 9, 6, 5, 1, 4, 2)
//ture numbers.forall(n => n < 10)
//false numbers.forall(n => n > 5)
The function forall
is created for this sort of task.
#8 Partitioning of a Collection
What if you have a plan to separate a collection into two new collections by some rule? This can be done with help of the partition
function. So let’s put all even numbers in one collection and all odd numbers in another:
val numbers = Seq(3, 7, 2, 9, 6, 5, 1, 4, 2)
//(List(2, 6, 4, 2), List(3, 7, 9, 5, 1))
numbers.partition(n => n % 2 == 0)
#9 Fold?
Another one popular operation is fold
. In the context of Scala, you can usually think about foldLeft
and foldRight
. In general, they do the same job but from the different sides:
val numbers = Seq(1, 2, 3, 4, 5)
//15 numbers.foldLeft(0)((res, n) => res + n)
The code sample above needs some explanation. In the first pair of parentheses, we put a start value. In the second pair of parentheses, we define the operation that needs to be performed for each element of the numbers
sequence. On the first step, n = 0, then it evolves according to the sequence elements.
Just to clarify, I want to provide another example of foldLeft
. Let’s count the number of characters in a sequence of words:
val words = Seq("apple", "dog", "table")
//13 words.foldLeft(0)((resultLength, word) => resultLength + word.length)
#10 Your Favorite Function
After a so long enumeration, it would be cool to see your favorite function from Scala collections. Write about it in the comments and provide an example of its usage.
Cooperation Among Functions
As I promised in the beginning of this post, I'll provide an example of function composition. Recently, I was passing a Codility test and there was a task:
Given string S, you have to find the longest substring that contains uppercase and lowercase characters, but not numbers.
Example: dP4knqw1QAp
Answer: QAp
So how do we solve this task with help of Scala collection functions?
def theLongest(s: String): String = {
s.split("[0-9]")
.filter(str => str.exists(ch => ch.isUpper))
.maxBy(str => str.length) }
This function solves the problem. If the input string doesn’t contain any suitable substring, then UnsupportedOperationException
will be thrown.
Summary
Scala has an incredibly powerful collection API. You can do a lot of stuff with it. Furthermore, the same things can be done in different ways, e.g. look at the section about Euler functions. The API is rich and its study requires time and practicing.
Published at DZone with permission of Alexey Zvolinskiy, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments