Tuples and Maps in Scala
This post is part 6 of the Functional Programming in Scala series. You can view all the posts in the series here.
A Tuple is basically grouping data of different types.
- To create a tuple we can do
val tuple = (43, "James Willett")
- To access the members of a tuple:
- To copy a tuple and change some values:
tuple.copy(_1 = 0)
- To pretty print them:
- And to swap the values:
Maps are a useful data structure to make associations between keys and values.
- We can create a map like so:
val myMap = Map("John" -> 234, "Mary" -> 7879)
- Check a value exists in the map -
- Access values in the map -
- Add a new pairing to the map by creating a new one:
val anotherMap = myMap + ("Bob", 123)
mapValues functionals are very useful for accessing data in maps.
filter are also used on the pairings of maps.
We can transform maps to and from other collections, with methods like
Tuples in Scala are similar to lists, they are finite orders of data. A tuple can be used to group together data of different types, for example Strings and Integers:
val aTuple = new Tuple2(2, "hello, Scala")
The type of the above tuple is
Tuple2[Int, String]. This is known to Scala as simply
(Int, String). The parentheses is syntactic sugar for Tuple2 type parameterised with Int and String.
In Scala you can create tuples either with or without the
new keyword. Specifying the
new keyword isn’t necessary as each Tuple companion object has an appropriate
apply method. So you can create a tuple like this:
val aTuple = Tuple2(2, "hello, Scala")
Or, you can actually create tuples with just parentheses:
val aTuple = (2, "hello, Scala")
Tuples can group at most 22 elements of different types. The reason for the number of elements being 22, is because they are used in conjunction with Function Types.
You can access members of a tuple like so:
_1 is the first element,
_2 is the second element etc.
We can create copies, just as we did with case classes:
println(aTuple.copy(_2 = "hello, James"))
There is also a
swap method, that swaps the elements in place:
Maps are collections used to associate things with other things. With maps, we associate keys to values. Keys are the index, values are the data corresponding to those keys:
val aMap: Map[String, Int] = Map()
In the map above,
String is the key and
Int is the value. We are instantiating the
Map() by calling the
Let’s create a map, in which we put tuples into the map.
val phonebook = Map(("James", 555), ("Bob", 666))
You populate the map by specifying tuples, or pairings.
For a clearer, syntactic sugar for the pairings, you can also write the tuples with an
val phonebook = Map("James" -> 555, "Bob" -> 666)
If we print out the map now, it will look like this:
println(phonebook) // prints out: 'Map(James -> 555, Bob -> 666)'
We can query a map if it has a key. The
contains method takes a key and returns a boolean:
To obtain the value associated with the key, we can call the
Or we can leave the apply word out altogether:
If you try to call the apply method on a map with a key that does not exist, the program will crash with an exception:
This throws the exception:
Exception in thread “main” java.util.NoSuchElementException: key not found: Ted
A way to guard against throwing these exceptions, is to create the map with the
val phonebook = Map(("James", 555), "Bob" -> 666).withDefaultValue(-1)
To add a pairing to the map, first we create the pairing:
val newPairing = "Mary" -> 678
And then we add the pairing to the phonebook, by creating a new phonebook and concatenating:
val newPhonebook = phonebook + newPairing
We use this technique of creating a new object, because maps are immutable.
There are several different functionals that we can use on Map.
Just like we did with lists, we can use
filter on maps.
For map, the argument that is supplied is a pairing which is a tuple:
println(phonebook.map(pair => pair._1.toLowerCase -> pair._2)) // returns: Map(james -> 555, bob -> 666)
What the above says is that, for every pairing in the map, transform the map by lowercasing just the keys.
With Maps in Scala, the methods
filter all take pairings like this.
A more useful method for maps is
println(phonebook.filterKeys(x => x.startsWith("J")))
The above will print the mapping where the keys are filtered by the predicate
x => x.startsWith("J").
This could be written in shorthand like this:
Another map specific function that is very useful is
println(phonebook.mapValues(number => number * 10))
So here the lambda takes the value type (in this case Int) and returns something else (in this case 10 times the number).
As another example, You could use this to put some text in front of the number:
println(phonebook.mapValues(number => "0845-" + number )) // returns: Map(James -> 0845-555, Bob -> 0845-666)
We can put all the pairings from a map into a list:
println(phonebook.toList) // returns: List((James,555), (Bob,666))
Conversions can also happen vice versa. We pass in a tuple for a single element and convert that into a map:
println(List(("Daniel", 555)).toMap) // returns: Map(Daniel -> 555)
Something else very useful is the
val names = List("Bob", "James", "Angela", "Mary", "Daniel", "Jim") println(names.groupBy(name => name.charAt(0)))
This will create a map, that will group every element that has the same first character into a single list.
So the above returns:
Map(J -> List(James, Jim), A -> List(Angela), M -> List(Mary), B -> List(Bob), D -> List(Daniel))
groupBy is very used in practice for grouping data.
As always, the source code for this post is available on Github.