Diving Into Scala Maps
Let's explore Scala maps, and the myriad ways you can iterate and transform over them while learning important lessons about their mutability.
Join the DZone community and get the full member experience.
Join For FreeA Scala Map is a collection of a Key-value pair. A map cannot have duplicate keys, but different keys can have same values. Maps in Scala are not language syntax. They are library abstractions that you can extend and adapt.
Scala provides mutable and immutable alternatives for maps. The class hierarchy for Scala maps is shown below:
Image is taken from Programming in Scala by Martin Odersky
There’s a base Map trait in package scala.collection and two subtraits – a mutable Map in scala.collection.mutable and an immutable one in scala.collection.immutable. By default, a Scala Map is immutable, but if you want to use mutable one, then you need to import mutable Map by using the statement: import scala.collection.mutable
Creating and initializing a map is easy:
val tasks = Map(1 -> “Book movie tickets”,2 -> “Go shopping”)
The Scala compiler transforms a binary operation expression like 1 -> “Book movie ticket” into (1).->(“Book movie ticket”). Thus, when you say 1 -> “Book movie ticket”, you are actually calling a method named -> on an integer with the value 1, passing in a string with the value “Book movie ticket”. This -> method, which you can invoke on any object in a Scala program, returns a two-element tuple containing the key and value.
Maps are Iterables of pairs of keys and values (also named mappings or associations). Scala’s Predef object offers an implicit conversion that lets you write key -> value as an alternate syntax for the pair (key, value). Therefore, Map(1 -> “Book movie ticket” , 2 ->”Go shopping”) means exactly the same as Map((1,”Book movie ticket”),(2,”Go shopping”))
As for creating a mutable map:
import scala.collection.mutable
val tasks = mutable.Map[Int,String]()
tasks += (1 -> “Book a movie ticket”)
tasks += (2 -> “Go shopping”)
Basic Map Operations
- keys: returns an iterable containing each key in the map.
- values: returns an iterable containing each value in the map.
- isEmpty: returns true if the map is empty otherwise false.
For example:
val subjects = Map(1 -> “DS”,2 -> “C++”,3 -> “Java”)
subjects.keys
subjects.values
subjects.isEmpty
Will give the result:
res0: Iterable[Int] = Set(1, 2, 3)
res1: Iterable[String] = MapLike.DefaultValuesIterable(DS, C++, Java)
res2: Boolean = false
Map operations fall into the following categories.
Lookups
These operations turn maps into partial functions from keys to values. Lookup operations include apply, get, getOrElse, contains, and isDefinedAt. The fundamental lookup method for a map is:
def get(key): Option[Value]
The operation “m get key” tests whether the map contains an association for the given key. If so, it returns the associated value in a Some. If no key is defined in the map, get returns None. Maps also define an apply method that returns the value associated with a given key directly, without wrapping it in an Option. If the key is not defined in the map, an exception is raised.
Let’s say we have a Map:
val subjects = Map(1 -> “DS”,2 -> “C++”,3 -> “Java”)
TheGet operation will return the value associated with the key in map subjects, such as an option, or None if not found.
subjects get(1) will result in res0: Option[String] = Some(DS)
.
The apply operation uses the value associated key in map subjects, or throws an exception if not found.
subjects(1) will result in res1: String = DS
.
getOrElse: The value associated key in the map, or the default value if not found.
subjects getOrElse(4,”DCN”) will result in res2: String = DCN
.
Additions and Updates
These operations let you add new bindings to a map or change existing bindings. + , ++ , and updated are included in this category.
Examples:
- + operation: Used to add single element
subjects + (4 ->”Operating system”)
will result in:
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> DS, 2 -> C++, 3 -> Java, 4 -> Operating system)
- ++ operation: Used to add multiple Maps
val subjects = Map(1 -> “DS”,2 -> “C++”,3 -> “Java”)
val newSubjects = Map(3 -> “Scala”, 4 -> “c#”)
val listOfSubjects = subjects ++ newSubjects
will result in:
listOfSubjects: scala.collection.immutable.Map[Int,String] = Map(1 -> DS, 2 -> C++,
3 -> Scala, 4 -> c#)
- updated operation: To update the value of a particular key.
subjects updated(1,”OS”)
will result in:
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> OS, 2 -> C++, 3 -> Java)
Removals
These operations remove bindings from a map. They include – and —
For example:
- – operation: Use to remove one element from Map.
val subjects = Map(1 -> “DS”,2 -> “C++”,3 -> “Java”)
subjects – 1
will result in:
res0: scala.collection.immutable.Map[Int,String] = Map(2 -> C++, 3 -> Java)
Subcollection Producers
These return a map’s keys and values separately in various forms. They include keys, keySet, keysIterator, valuesIterator, and values.
For example, take:
val subjects = Map(1 -> “DS”,2 -> “C++”,3 -> “Java”)
- Keys returns an iterable containing each key in the map
subjects.keys
will result in:
res0: Iterable[Int] = Set(1, 2, 3)
keySet returns a set containing each key in the map
subjects.keySet
will result in:
res1: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
- keysIterator: returns an iterator over all keys in the map
subjects.keysIterator.toSet
will result in:
res2: Iterator[Int] = non-empty iterator
- valuesIterator: returns an iterator over all values that are associated with some key in the map
subjects.valuessIterator.toSet
will result in:
res3: Iterator[String] = non-empty iterator
Transformations
Transformations produce a new map by filtering and transforming bindings of an existing map.They include filterKeys and mapValues
Example:
- filterKeys: Filters the map by retaining only keys satisfying a predicate.
val subjects = Map(1 -> “DS”,2 -> “C++”,3 -> “Java”)
subjects.filterKeys(x => x%2 == 0)
will result in:
res0: scala.collection.immutable.Map[Int,String] = Map(2 -> C++)
- mapValues: Transforms the map by applying a function to every retrieved value.
val values = Map( “a” -> 1, “b” -> 2 )
values.mapValues(_ * 2)
will result in:
res1: scala.collection.immutable.Map[String,Int] = Map(a -> 2, b -> 4)
Concatenating Two Maps
To concatenate two or more maps, we use the ++ operator or Map.++() method. While doing so, duplicate keys are removed and only the last value will be stored in the map. For example:
val subjects = Map(1 -> “DS”,2 -> “C++”,3 -> “Java”)
val newSubjects = Map(3 -> “Scala”, 4 -> “c#”)
subjects ++ newSubjects
This will result in:
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> DS, 2 -> C++, 3 -> Scala, 4 -> c#)
Duplicate key 3, having value java, is replaced by a new value of 3.
Iterating Over Scala Maps
You can iterate over Map using several different techniques. For example, we have...
- For loops:
val subjects = Map(1 -> “DS”,2 -> “C++”,3 -> “Java”)
for ((key, value) <- subjects){
println(“key is : ” + key + ” its vaue is : ” + value)
}
- Foreach loops:
val subjects = Map(1 -> “DS”,2 -> “C++”,3 -> “Java”)
subjects foreach{case (key,value) =>
println(“key is : ” + key +” its vaue is : ” + value)}
Understanding Mutable Variables With Immutable Collections
Mixing a mutable variable (var) with an immutable collection causes surprising behavior. While creating an immutable Map as a var, it appears you can somehow add new elements to it:
var subjects = Map(1 -> “DS”)
subjects += (2 -> “C++”)
subjects += (3 -> “Java”)
It looks like you’re mutating an immutable collection, but what’s really happening is that the subjects variable points to a new collection each time you use the += method.
The subjects variable is mutable — like a non-final field in Java — so it’s actually being reassigned to a new collection during each step.
The end result is as follows:
var subjects = Map(1 -> “DS”)
subjects += (1 -> “DS”,2 -> “C++”)
subjects += (1 -> “DS”,2 -> “C++”,3 -> “Java”)
When you first start working with Scala, the behavior of a mutable variable with an immutable collection can be surprising. To be clear about variables and values:
- A mutable variable (var) can be reassigned to point at new data.
- An immutable variable (val) is like a final variable in Java; it can never be reassigned.
Published at DZone with permission of Shivangi Gupta. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Implementing a Serverless DevOps Pipeline With AWS Lambda and CodePipeline
-
How To Use Pandas and Matplotlib To Perform EDA In Python
-
Operator Overloading in Java
-
Exploratory Testing Tutorial: A Comprehensive Guide With Examples and Best Practices
Comments