Table of Contents
- The Scala Map class
- A note about the Map examples
- CREATE: How to create an immutable Scala Map class instance
- READ: How to loop over a Scala Map with 'for' and foreach
- READ: Getting keys and values from a Map
- READ: How to access Map elements
- UPDATE: Adding elements to an immutable Map (+, ++)
- UPDATE: How to update immutable Map elements
- DELETE: Removing elements from a Map (-, --)
- MORE: Test for existence of keys or values in a Map
- MORE: Map filtering methods
- MORE: Map transformer methods
- MORE: Informational and mathematical methods
- MORE: Grouping methods
- More Map implementations (sorted, linked, hash, weak)
- MORE: How to convert Java Map to Scala Map
- Scala Map class summary
This page contains a large collection of examples of how to use the Scala Map class. There are currently well over 100 Map
examples.
The Scala Map class
A Scala Map
is a collection of unique keys and their associated values — i.e., a collection of key/value pairs — similar to a Java Map
, Ruby Hash
, or Python dictionary.
On this page I’ll demonstrate examples of the immutable Scala Map
class. If you’re new to the immutable world, “immutable” means that you can’t change the contents of the map, and you can’t resize it. The way you work with an immutable map is to specify updates to it as you assign the changed result to a new variable.
A note about the Map examples
The following examples are demonstrated in a “CRUD+” order, meaning:
- C — Create
- R — Read
- U — Update
- D — Delete
- + — demonstrates many methods available on the
List
class
CREATE: How to create an immutable Scala Map class instance
The immutable Map
class is in scope by default, so you can create an immutable map without an import statement:
val states = Map(
"AK" -> "Alaska",
"AL" -> "Alabama",
"AR" -> "Arizona"
)
Here’s what it looks like in the Scala REPL:
scala> val states = Map(
| "AK" -> "Alaska",
| "AL" -> "Alabama",
| "AR" -> "Arizona"
| )
states: scala.collection.Map[String,String] = Map(AK -> Alaska, AL -> Alabama, AR -> Arizona)
Note about the syntax
The syntax that’s used inside the parentheses in a map creates a Tuple2
:
"AL" -> "Alabama" # (String, String) = (AL,Alabama)
Because you can also declare a Tuple2
as ("AL", "Alabama")
, you may (rarely) see maps created like this:
val states = Map(
("AK", "Alaska"),
("AL", "Alabama"),
("AR", "Arizona")
)
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
READ: How to loop over a Scala Map with 'for' and foreach
Here are some examples that show how to loop/iterate over Map elements using for
and foreach
:
val map = Map(
1 -> "one",
2 -> "two",
3 -> "three"
)
# for
scala> for ((k,v) <- map) printf("key: %s, value: %s\n", k, v)
key: 1, value: one
key: 2, value: two
key: 3, value: three
# foreach (tuples)
scala> map.foreach(x => println (x._1 + "-->" + x._2))
1-->one
2-->two
3-->three
# foreach (foreach and case)
scala> map.foreach {case (key, value) => println (key + "-->" + value)}
1-->one
2-->two
3-->three
# foreach key
scala> map.keys.foreach(println)
1
2
3
# foreach value
scala> map.values.foreach(println)
one
two
three
READ: Getting keys and values from a Map
Given this map:
val states = Map(
"AK" -> "Alaska",
"AL" -> "Alabama",
"AR" -> "Arkansas"
)
How to get Map
keys:
states.keySet # Set[String] = Set(AK, AL, AR)
states.keys # Iterable[String] = Set(AK, AL, AR)
states.keysIterator # Iterator[String] = non-empty iterator
How to get Map
values:
states.values # Iterable[String] = MapLike.DefaultValuesIterable(Alaska, Alabama, Arkansas)
states.valuesIterator # Iterator[String] = non-empty iterator
READ: How to access Map elements
Access map elements by their keys as shown here, but beware that this approach can throw an NoSuchElementException
:
val states = Map(
"AK" -> "Alaska",
"AL" -> "Alabama",
"AZ" -> "Arizona"
)
val az = states("AZ") # Arizona
val foo = states("FOO") # java.util.NoSuchElementException: key not found: FOO
Solutions for NoSuchElementException
Possible solutions:
- Use
withDefaultValue(a)
with a sub-type ofMap
get
(returns anOption
)getOrElse
contains
isDefinedAt
Use withDefaultValue(a)
with a sub-type of Map
The base immutable Map trait doesn’t have a withDefaultValue
method, but some of its subclasses do:
# SortedMap
import collection.immutable.SortedMap
val a = SortedMap(1 -> "one") # SortedMap[Int,String] = Map(1 -> one)
a(1) # 1
a(2) # throws a java.util.NoSuchElementException exception
val a = SortedMap(1 -> "one").withDefaultValue("not found")
a(1) # 1
a(2) # not found
# ListMap
import collection.immutable.ListMap
val a = ListMap(1 -> "one").withDefaultValue("not found")
a(1) # 1
a(2) # not found
Other possible solutions
The following examples assume you have this map:
val states = Map(
"AK" -> "Alaska",
"AL" -> "Alabama"
)
get
:
states.get("AK") # Some(Alaska)
states.get("foo") # None
getOrElse
:
states.getOrElse("AK", "No such state") # Alaska
states.getOrElse("foo", "No such state") # No such state
contains
and isDefinedAt
:
states.contains("AK") # true
states.contains("foo") # false
states.isDefinedAt("AK") # true
states.isDefinedAt("foo") # false
Can do this, but it’s just a long form of getOrElse
:
if (states.contains(maybeState)) states(maybeState) else "No such state"
UPDATE: Adding elements to an immutable Map (+, ++)
Solution: Use the +
or ++
methods.
+ examples
Add one or more key/value pairs with +
(while assigning the result to a new val
):
val a = Map(1 -> "one") # Map(1 -> one)
val b = a + (2 -> "two") # Map(1 -> one, 2 -> two)
val c = b + (
3 -> "three",
4 -> "four"
)
# c: Map(1 -> one, 2 -> two, 3 -> three, 4 -> four)
In the OOP world you can also create a variable as a var
and then reassign the result back to the same variable name:
var a = Map(1 -> "one") # Map(1 -> one)
a = a + (2 -> "two") # Map(1 -> one, 2 -> two)
a = a + (
3 -> "three",
4 -> "four"
)
# a: Map(1 -> one, 2 -> two, 3 -> three, 4 -> four)
++ examples
Use ++
to add a Traversable[(K,V)]
to the map:
val a = Map(
1 -> "one",
2 -> "two",
)
val b = Map(
3 -> "three",
4 -> "four"
)
a ++ b # Map(1 -> one, 2 -> two, 3 -> three, 4 -> four)
val c = List(
3 -> "three",
4 -> "four"
)
a ++ c # Map(2 -> two, 4 -> four, 1 -> one, 3 -> three)
UPDATE: How to update immutable Map elements
Use updated
:
val a = Map(
1 -> "one",
2 -> "two",
3 -> "three"
)
val b = a.updated(3, "THREE!") # Map(1 -> one, 2 -> two, 3 -> THREE!)
DELETE: Removing elements from a Map (-, --)
Use -
or --
.
- examples
Remove one or more elements by key with -
:
val a = Map(
1 -> "one",
2 -> "two",
3 -> "three",
4 -> "four"
)
a - 4 # Map(1 -> one, 2 -> two, 3 -> three)
a - 3 - 2 # Map(1 -> one, 4 -> four)
-- examples
Use --
to remove multiple elements by specifying the keys in a sequence (technically a GenTraversableOnce[K]
, like Array, ArrayBuffer, Seq, Vector, Set, etc.):
val a = Map(
1 -> "one",
2 -> "two",
3 -> "three",
4 -> "four"
)
a -- Array(3,4) # Map(1 -> one, 2 -> two)
a -- Seq(1,2) # Map(3 -> three, 4 -> four)
a -- Set(1,4) # Map(2 -> two, 3 -> three)
MORE: Test for existence of keys or values in a Map
Given this map:
val states = Map(
"AK" -> "Alaska",
"IL" -> "Illinois",
"KY" -> "Kentucky"
)
Test for existence of a key:
states.contains("foo")) # false
Or get the keys as a set, iterable, or iterator, which you can then search:
states.keySet # Set[String] = Set(AK, AL, AR)
states.keys # Iterable[String] = Set(AK, AL, AR)
states.keysIterator # Iterator[String] = non-empty iterator
Test for existence of a value:
states.valuesIterator.exists(_.contains("Alaska")) # true
states.valuesIterator.exists(_.contains("foo")) # false
MORE: Map filtering methods
This lesson covers the immutable Map class, so you don’t remove elements from it. Instead, you describe how to remove elements as you apply a method to the map and assign the results to a new collection.
Method | Returns |
---|---|
drop(n) |
All elements after the first n elements |
dropRight(n) |
All elements except the last n elements |
dropWhile(p) |
Drop the first sequence of elements that matches the predicate p |
filter(p) |
All elements that match the predicate p |
filterNot(p) |
All elements that do not match the predicate p |
filterKeys(p) |
All elements where the keys match the predicate p |
find(p) |
The first element that matches the predicate p |
head |
The first element; can throw an exception if the Map is empty |
headOption |
The first element as an Option |
init |
All elements except the last one |
last |
The last element; can throw an exception if the Map is empty |
lastOption |
The last element as an Option |
slice(f,u) |
A sequence of elements from index f (from) to index u (until) |
tail |
All elements after the first element |
take(n) |
The first n elements |
takeRight(n) |
The last n elements |
takeWhile(p) |
The first subset of elements that matches the predicate p |
Examples
Examples of filtering methods on a Scala Map:
val a = Map(
1 -> "one",
2 -> "two",
3 -> "three"
)
a.drop(1) # Map(2 -> two, 3 -> three)
a.dropRight(1) # Map(1 -> one, 2 -> two)
a.dropWhile(t => t._1 < 3) # Map(3 -> three)
a.filter(t => t._1 > 1) # Map(2 -> two, 3 -> three)
a.filter(t => t._2 == "two") # Map(2 -> two)
a.filterNot(t => t._1 > 1) # Map(1 -> one)
a.filter { case (k,v) =>
v == "two"
}
# result: Map(2 -> two)
a.filterKeys(_ > 1) # Map(2 -> two, 3 -> three)
a.filterKeys(Set(1,3)) # Map(1 -> one, 3 -> three)
a.find(t => t._1 > 1) # Some((2,two))
a.head # (1,one)
a.headOption # Some((1,one))
a.init # Map(1 -> one, 2 -> two)
a.last # (3,three)
a.lastOption # Some((3,three))
a.slice(0,0) # Map()
a.slice(0,1) # Map(1 -> one)
a.slice(0,2) # Map(1 -> one, 2 -> two)
a.slice(1,1) # Map()
a.slice(1,2) # Map(2 -> two)
a.slice(1,3) # Map(2 -> two, 3 -> three)
a.tail # Map(2 -> two, 3 -> three)
a.take(1) # Map(1 -> one)
a.takeRight(1) # Map(3 -> three)
a.takeWhile(t => t._1 < 3) # Map(1 -> one, 2 -> two)
A note about the Map/filter syntax
Note that although the Scaladoc shows this signature for filter
:
def filter(p: ((K, V)) ⇒ Boolean): Map[K, V]
this syntax does not work:
scala> a.filter( (k,v) => k > 1 )
<console>:13: error: missing parameter type
Note: The expected type requires a one-argument function accepting a 2-Tuple.
Consider a pattern matching anonymous function, `{ case (k, v) => ... }`
a.filter( (k,v) => k > 1 )
^
MORE: Map transformer methods
A transformer method is a method that constructs a new collection from an existing collection.
Method | Returns |
---|---|
collect(pf) |
Applies the partial function pf to all elements of the map, returning elements for which the function is defined |
collectFirst(pf) |
Applies the partial function pf to the first element of the map for which the partial function pf is defined, and returns the result of its application |
flatten |
TODO |
flatMap(f) |
TODO |
map(f) |
A new map by applying the function f to each element in the map |
mapValues(f) | A new map by applying the function f to each value in the map |
to* |
A series of methods that transform the map to other collections |
updated(k,v) |
A new map with the value at key k replaced with the new value v |
zipAll(i,a,b) |
TODO i = Iterable, a = type A , b = type B |
zip(c) |
A map created by combining corresponding elements with the given Iterable collection c |
zipWithIndex |
A map created by zipping this map with its indices |
val a = Map(
1 -> "one",
2 -> "two",
3 -> "three"
)
a.collect{case (k,v) => (k*2, v)} # Map(2 -> one, 4 -> two, 6 -> three)
a.collect{case (k,v) => k > 1} # List(false, true, true)
a.collectFirst{case (k,v) => (k*2, v)} # Some((2,one))
a.map(t => (t._1*2, t._2)) # Map(2 -> one, 4 -> two, 6 -> three)
a.map(t => t._1*2) # List(2, 4, 6)
a.map{case (k,v) => (k*2, v)} # Map(2 -> one, 4 -> two, 6 -> three)
a.map{case (k,v) => k*2} # List(2, 4, 6)
a.map{case (k,v) => v.toUpperCase} # List(ONE, TWO, THREE)
a.mapValues(_.toUpperCase) # Map(2 -> TWO, 1 -> ONE, 3 -> THREE)
a.toArray # Array[(Int, String)] = Array((2,two), (1,one), (3,three))
a.toBuffer # mutable.Buffer[(Int, String)] = ArrayBuffer((2,two), (1,one), (3,three))
a.toIndexedSeq # immutable.IndexedSeq[(Int, String)] = Vector((2,two), (1,one), (3,three))
a.toIterable # Iterable[(Int, String)] = Map(2 -> two, 1 -> one, 3 -> three)
a.toIterator # Iterator[(Int, String)] = non-empty iterator
a.toList # List[(Int, String)] = List((2,two), (1,one), (3,three))
a.toSeq # Seq[(Int, String)] = ArrayBuffer((2,two), (1,one), (3,three))
a.toSet # immutable.Set[(Int, String)] = Set((2,two), (1,one), (3,three))
a.toStream # immutable.Stream[(Int, String)] = Stream((2,two), ?)
a.toString # String = Map(2 -> two, 1 -> one, 3 -> three)
a.toTraversable # Traversable[(Int, String)] = Map(2 -> two, 1 -> one, 3 -> three)
a.toVector # Vector[(Int, String)] = Vector((2,two), (1,one), (3,three))
a.updated(1, "ONE!") # Map(1 -> ONE!, 2 -> two, 3 -> three)
a.zip(Seq(10,20,30)) # Map((1,one) -> 10, (2,two) -> 20, (3,three) -> 30)
a.zip(Map(
"a" -> "aa",
"b" -> "bee"
))
# result: mutable.Map[(Int, String),(String, String)] = Map((1,one) -> (a,aa), (2,two) -> (b,bee))
a.zipWithIndex # Map((1,one) -> 0, (2,two) -> 1, (3,three) -> 2)
MORE: Informational and mathematical methods
Informational and mathematical methods let you obtain information about a collection and the contents of the collection.
Method | Returns |
---|---|
canEqual(a) |
True if the map “can equal” the parameter a of base type Any (should be defined by user-defined subclasses) |
contains(k) |
True if the map contains the key k |
count(p) |
The number of elements in the map for which the predicate is true |
exists(p) |
True if the predicate returns true for at least one element in the map |
find(p) |
The first element that matches the predicate p , returned as an Option |
forall(p) |
True if the predicate p is true for all elements in the map |
hasDefiniteSize |
True if the map has a finite size |
head |
The first element of the map (which can/will vary depending on the map type); can throw NoSuchElementException |
headOption |
The first element of the map returned as an Option (which can/will vary depending on the map type) |
isDefinedAt(k) |
True if the map contains the index k |
isEmpty |
True if the map contains no elements |
last |
The last element of the map (which can/will vary depending on the map type); can throw NoSuchElementException |
lastOption |
The last element of the map returned as an Option (which can/will vary depending on the map type) |
max |
The largest element in the map |
min |
The smallest element in the map |
nonEmpty |
True if the map is not empty (i.e., if it contains 1 or more elements) |
product |
The result of multiplying the elements in the map |
size |
The number of elements in the map |
sum |
The sum of the elements in the map |
fold(s)(o) |
“Fold” the elements of the map using the binary operator o , using an initial seed s (see also reduce ) |
foldLeft(s)(o) |
“Fold” the elements of the map using the binary operator o , using an initial seed s , going from left to right (see also reduceLeft ) |
foldRight(s)(o) |
“Fold” the elements of the map using the binary operator o , using an initial seed s , going from right to left (see also reduceRight ) |
reduce |
“Reduce” the elements of the map using the binary operator o |
reduceLeft |
“Reduce” the elements of the map using the binary operator o , going from left to right |
reduceRight |
“Reduce” the elements of the map using the binary operator o , going from right to left |
Examples
Examples:
val a = Map(
1 -> "one",
2 -> "two",
3 -> "three"
)
# `canEqual` should be defined by user-defined subclasses
a.canEqual("foo") # true
a.canEqual(1) # true
a.canEqual(null) # true
a.contains(1) # true
a.contains(0) # false
a.count(t => t._1 > 1) # 2
a.count{case (k,v) => k > 1} # 2
a.exists(t => t._1 > 1) # true
a.exists{case (k,v) => k > 1} # true
a.find(t => t._1 > 1) # Some((2,two))
a.find{case (k,v) => k > 1} # Some((2,two))
a.forall(t => t._1 > 0) # true
a.forall(t => t._1 > 33) # false
a.forall{case (k,v) => k > 0} # true
a.hasDefiniteSize # true
a.head # (1,one)
a.headOption # Some((1,one))
a.isDefinedAt(0) # 0
a.isDefinedAt(1) # 1
a.isEmpty # false
a.last # (3,three)
a.lastOption # Some((3,three))
max # (3,three)
min # (1,one)
a.nonEmpty # true
product TODO
a.size # 3
a.sum
# foldLeft expects your algorithm to return a type that matches the map key type
a.foldLeft(0) {
case(i, (k,v)) => {println(s"i = $i, k = $k"); k+i }
}
# result:
i = 0, k = 2
i = 2, k = 1
i = 3, k = 3
res0: Int = 6
a.foldLeft(0) {
case(acc, (k,v)) => {
val result = k + acc
println(s"acc = $acc, k = $k, v = $v, result = $result")
result
}
}
acc = 0, k = 2, v = two, result = 2
acc = 2, k = 1, v = one, result = 3
acc = 3, k = 3, v = three, result = 6
res1: Int = 6
MORE: Grouping methods
These methods generally let you create multiple groups from a collection.
Method | Returns |
---|---|
groupBy(f) |
A map of collections created by the function f |
grouped |
Breaks the map into fixed-size iterable collections |
partition(p) |
Two traversable collections created by the predicate p |
sliding(i,s) |
Group elements into fixed size blocks by passing a sliding window of size i and step s over them |
span(p) |
A tuple of two collections; the first created by sequence.takeWhile(p) , and the second created by sequence.dropWhile(p) |
splitAt(i) |
A tuple of two maps by splitting the sequence at index i |
unzip(asPair) |
TODO Two collections of the first and second half of each pair, using the implicit parameter asPair |
unzip3 |
TODO |
a.groupBy(t => t._1 > 99)
// Map[Boolean,Map[Int,String]] = Map(false->Map(1->one, 2->two, 3->three))
a.groupBy{case (k,v) => k > 1}
// Map[Boolean,Map[Int,String]] = Map(false->Map(1->one), true->Map(2->two, 3->three))
a.grouped(2)
// Iterator[Map[Int,String]] = non-empty iterator
a.partition(t => t._1 > 99)
// Map[Int,String], Map[Int,String]) = (Map(), Map(1->one, 2->two, 3->three))
a.sliding(2)
// Iterator[Map[Int,String]] = non-empty iterator
scala> a.sliding(1,1)
// Iterator[Map[Int,String]] = non-empty iterator
a.span(t => t._1 > 99)
// (Map[Int,String], Map[Int,String]) = (Map(),Map(1->one, 2->two, 3->three))
a.splitAt(1)
// (Map[Int,String], Map[Int,String]) = (Map(1->one), Map(2->two, 3->three))
a.splitAt(2)
(Map[Int,String], Map[Int,String]) = (Map(1->one, 2->two),Map(3->three))
More Map implementations (sorted, linked, hash, weak)
The basic (common) Scala map classes and traits:
Class or Trait | Description |
---|---|
collection.immutable.Map | The default, general-purpose immutable map you get if you don’t import anything. |
collection.mutable.Map | A mutable version of the basic map. |
collection.mutable.LinkedHashMap | “All methods that traverse the elements will visit the elements in their insertion order.” |
collection.immutable.ListMap collection.mutable.ListMap |
Per the Scaladoc, “implements immutable maps using a list-based data structure.” As shown in the examples, elements that are added are prepended to the head of the list. |
collection.SortedMap | Keys of the map are returned in sorted order. Therefore, all traversal methods (such as foreach ) return keys in that order. |
More Map
types:
Class or Trait | Description |
---|---|
collection.immutable.HashMap | From the Scaladoc, “implements immutable maps using a hash trie.” |
collection.mutable.ObservableMap | From the Scaladoc: “This class is typically used as a mixin. It adds a subscription mechanism to the Map class into which this abstract class is mixed in.” |
collection.mutable.MultiMap | From the Scaladoc: “A trait for mutable maps with multiple values assigned to a key.” |
collection.mutable.SynchronizedMap | From the Scaladoc: This trait “should be used as a mixin. It synchronizes the map functions of the class into which it is mixed in.” |
collection.immutable.TreeMap | From the Scaladoc: “implements immutable maps using a tree.” |
collection.mutable.WeakHashMap | A wrapper around java.util.WeakHashMap, “a map entry is removed if the key is no longer strongly referenced.” |
But wait, there’s still more (concurrent/parallel):
- collection.parallel.immutable.ParHashMap
- collection.parallel.mutable.ParHashMap
- collection.concurrent.TrieMap
Sorted Map
Keys are stored in sorted order:
import scala.collection.SortedMap
val grades = SortedMap(
"Kim" -> 90,
"Al" -> 85,
"Melissa" -> 95,
"Emily" -> 91,
"Hannah" -> 92
)
// REPL
scala> grades.foreach(println)
(Al,85)
(Emily,91)
(Hannah,92)
(Kim,90)
(Melissa,95)
Maps that remember the insertion order of elements
If you want a map that remembers the insertion order of its elements, use a LinkedHashMap or ListMap. Scala only has a mutable LinkedHashMap, and it returns its elements in the order you inserted them:
import scala.collection.mutable.LinkedHashMap
val states = LinkedHashMap("IL" -> "Illinois")
states += ("KY" -> "Kentucky")
states += ("TX" -> "Texas")
//REPL
scala> states.foreach(println)
(IL,Illinois)
(KY,Kentucky)
(TX,Texas)
Scala has both mutable and immutable ListMap classes. They return elements in the opposite order in which you inserted them, as though each insert was at the head of the map (like a List):
import scala.collection.mutable.ListMap
val states = ListMap("IL" -> "Illinois")
states += ("KY" -> "Kentucky")
states += ("TX" -> "Texas")
//REPL
scala> states.foreach(println)
(TX,Texas)
(IL,Illinois)
(KY,Kentucky)
MORE: How to convert Java Map to Scala Map
Convert a Java Map to a Scala Map using Scala’s JavaConverters object:
// import what you need
import java.util._
import scala.collection.JavaConverters._
// create and populate a java map
val jMap = new HashMap[String, String]()
jMap.put("first_name", "Alvin")
jMap.put("last_name", "Alexander")
// convert the java map to a scala map
val sMap = jMap.asScala
Can also convert Java Properties to a Scala Map:
import java.util._
val javaProps = new Properties
javaProps.put("first_name", "Adam")
javaProps.put("last_name", "Scott")
import scala.collection.JavaConverters._
val scalaProps = p.asScala
What that looks like in the REPL:
scala> val javaProps = new Properties
javaProps: java.util.Properties = {}
scala> javaProps.put("first_name", "Adam")
res2: Object = null
scala> javaProps.put("last_name", "Scott")
res3: Object = null
scala> val scalaProps = p.asScala
scalaProps: scala.collection.mutable.Map[String,String] =
Map(last_name -> Scott, first_name -> Adam)
More details:
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Scala Map class summary
I hope these Scala Map
class examples have been helpful. All of these examples will work with other immutable map classes that are subclasses of Map
, and the mutable Map
class will have other methods in addition to the methods shown here.