"The code that you write should absorb more meaning without becoming bloated or losing comprehensibility." - Anonymous In any programming language, one of the essential requirements is to have a way of working with a set of data, or, in other words, a collection of data. If you have worked with any programming language, you must already know the importance of its collection framework. Scala has a rich variety of collections; a rich set of helper functions makes it a lot easier to work with any Scala collection. In this chapter, we'll go through all the essentials of Scala collections. We'll be able to distinguish between several collection options, and also make efficient use of all collections. Along the way, we'll learn about: Before we start learning about immutable and mutable collections in Scala, we'll try to solve a simple problem using powerful methods provided by Scala collections. For that, let's take a look at a scenario: RESTful APIs As shown in the preceding image, we have a set of APIs with method types such as GET, POST, and PUT, and their associated URIs. As these are two entities (method and URI), think of all these as a list of tuples. Now we want to segregate them, so we can create a map, as shown in the right column of the preceding image. A map is a collection that stores values in a key-value pair. Hence, on the right side you can see API information as key-value pairs, where key is the method name, and the value is a list of URIs for that particular request type. So, the idea is to convert List[(String, String)] to Map[String, List[String]]. You may want to think about the solution, and come up with your own.
Meanwhile, let's see if Scala helps us in any way with our solution:
object RESTFulAPIs extends App { //List of Method and URI val listOfAPIs = List(("GET", "/user/:id"), ("GET", "user/:id/profile/:p_id"), ("POST", "/user"), ("POST", "/profile"), ("PUT", "/user/:id")) /* * Returns a scala.collection.immutable.Map[String, List[(String,String)]] */ val groupedListOfAPIs = listOfAPIs.groupBy(_._1) println(s"APIs grouped to a Map :: $groupedListOfAPIs") /* * Returns a scala.collection.immutable.Map[String, List[String]] */ val apisByMethod = groupedListOfAPIs.mapValues(_.map(_._2)) println(s"APIs By Method :: $apisByMethod") }
Here's the result:
APIs grouped to a Map :: Map(POST -> List((POST,/user), (POST,/profile)), GET -> List((GET,/user/:id), (GET,user/:id/profile/:p_id)), PUT -> List((PUT,/user/:id))) APIs By Method :: Map(POST -> List(/user, /profile), GET -> List(/user/:id, user/:id/profile/:p_id), PUT -> List(/user/:id))
If you have come up with a set of for loops or recursive methods to accomplish what can be done using a single method, you may want to rethink, or take a look at the solution we have here. Here, we used two utility methods that fulfill our purpose. The first one is groupBy, defined in the TraversableLike trait, which converts our List[(String, String)] to a Map[String, List[String]] grouped by the first element of the tuple, the method names. This groupBy operation gives us this:
Map(POST -> List((POST,/user), (POST,/profile)), GET -> List((GET,/user/:id), (GET,user/:id/profile/:p_id)), PUT -> List((PUT,/user/:id)))
The latter is the mapValues method from MapLike trait, which is used to wrap the given map with the same key. The value for each key is simply f(this(key)):
def mapValues[W](f: V => W): Map[K, W]
These two methods were enough to provide the solution, and helped us avoid many traversals using loops. This is just one example, and a lot can be done within only few lines of code that would otherwise have taken several lines. This really makes Scala collections powerful. Scala's collection framework is easy to use; most of the helper methods are universal, with a few exceptions. Also, there is no compromise with performance; these are performance-tuned methods. One can rely on these methods to accomplish any logic; it makes your code look nicer. But that's not all, it's just the beginning. Usually, collections are prone to code that is written with the current environment in mind. That usually makes it hard to debug what went wrong, specifically when mutable collections are in place. So, just to remove this complication, Scala has these immutable data collections. Once created, the immutable collections can't be updated. But how do they work, and how are they different from mutable collections? Let's go through and try to understand.
A collection is used to contain data that is used by the program later in time. In a multithreaded environment, if multiple threads try to access a collection at the same time, this can give you a hard time debugging what went wrong. That is a problem programmers usually face when working with collections in a multithreaded environment. But there's a universal solution for that, which expects you to use an immutable collection. Immutable means you can't change/mutate it. Scala provides you options to choose from: root, mutable, and immutable collections. These three are variants that exist in three separate packages: scala.collection, scala.collection.mutable, and scala.collection.immutable. If you don't specify the collection and use one, it'll be an immutable one by default. But how do these work, exactly? Let's take a look:
scala> val studentsPresent = List("Alex", "Bob", "Chris") studentsPresent: List[String] = List(Alex, Bob, Chris)
A collection that does not allow us to update or delete its elements is of not much use. So, why do we say these are rich collections? The reason is that even though these are immutable collections, there are ways to add and remove elements, but these actions return a new collection altogether. We'll see how these are constructed and then how adding an element affects the collection later in this chapter; but for now, it's important to know that immutable collections can be updated, although doing so returns another collection with the same set of elements, along with the updated collection.
On the other hand, we have mutable collections, which work similar to most object-oriented programming languages. You can declare and instantiate a collection with a few elements. Then, based on any requirements afterwards, you can change its elements, or remove them. With these mutable collections, Scala gives you a choice to make when selecting a collection to work with. When you use mutable collections, you get an extra set of methods to mutate the collection. Be sure, though, about the instances where you may mutate collections. That'll make your program world free of mutability complications.
The third variant, root collections, resides in the scala.collection package. When you use a root collection, it can be mutable or immutable. What does that mean? It means that a particular collection is a superclass of a collection from the same family residing in both the scala.collection.mutable and scala.collection.immutable packages. To understand what we just said, take a look at the following method:
def afunction(xs: scala.collection.Iterable[String]) = ???
The afunction function can take both mutable and immutable collections, as long as they are Iterable, which is one of the traits available in Scala's collection hierarchy.
There are few extra methods that let you mutate your collection, and as we may expect, those are defined only for collections in the scala.collection.mutable package, not the scala.collection or scala.collection.immutable packages. In that case, it's clear that while writing your root collection, the Scala compiler is not going to allow you to update your collection. We talked about one of the use cases of root collections where, regardless of the type of your collection, you can define a function—that i...
Citation styles for Learning Scala ProgrammingHow to cite Learning Scala Programming for your reference list or bibliography: select your referencing style from the list below and hit 'copy' to generate a citation. If your style isn't in the list, you can start a free trial to access over 20 additional styles from the Perlego eReader.
APA 6 Citation
Sharma, V. (2018). Learning Scala Programming (1st ed.). Packt Publishing. Retrieved from https://www.perlego.com/book/578842/learning-scala-programming-pdf (Original work published 2018)
Chicago Citation
Sharma, Vikash. (2018) 2018. Learning Scala Programming. 1st ed. Packt Publishing. https://www.perlego.com/book/578842/learning-scala-programming-pdf.
Harvard Citation
Sharma, V. (2018) Learning Scala Programming. 1st edn. Packt Publishing. Available at: https://www.perlego.com/book/578842/learning-scala-programming-pdf (Accessed: 14 October 2022).
MLA 7 Citation
Sharma, Vikash. Learning Scala Programming. 1st ed. Packt Publishing, 2018. Web. 14 Oct. 2022.