Scala Saturday – The map Method

One of the most frequently used operations in functional programming is the map operation. The map operation takes a collection of values, performs some transformation on each element, and creates a new collection containing those new elements. The elements in the new collection can be the same type as the elements in the original collection, or they may be of a different type. It just depends on the transformation.

Sequential Collections

Let’s look at a couple of examples. First, say that you have a list of integers, and you need the squares of those integers:

Illustrates mapping a squaring function over a list of integers (2, 4, 6, 8) to produce a new list of integers (4, 16, 36, 64)
Mapping a Squaring Function over a List of Integers

You can define the function f to calculate the square of a number. Then you can call the map method to apply f to each member in the input list and create a new list containing the squares of the items in the original list. In functional programming parlance, we say that you map f over the list to produce the new list. The code looks like this:

val f = (n: Int) => n * n
val squares = List(2,4,6,8) map f
// squares: List[Int] = List(4, 16, 36, 64)

Of course, you can also define the mapping function inline:

val squares = List(2,4,6,8) map { n => n * n }
// squares: List[Int] = List(4, 16, 36, 64)

Second, suppose you need to compute the length of each string in a list:

Illustrates mapping a function to calculate string length over a list of strings ("five", "six", "pick up", "sticks") to produce a list of integers (4, 3, 7, 6)
Mapping a Length Function over a List of Strings

Similar to the last example, just define a function to calculate the length of a string, and map it over the list of strings with the map method:

val lengths =
  List("five", "six", "pick up", "sticks") map {
    _.length
  }
// lengths: List[Int] = List(4, 3, 7, 6)

Notice two things:

  1. In both examples, the output list maintains the order of the elements in the input list. This happens because a list is a sequential collection. The same is also true for an array and a sequence. In contrast, as we will see in a moment, order is not maintained when mapping over a non-sequential collection like a set.
  2. Whereas the type of both the input list and the output list is the same in the first example—they both contain integers—in the second example, the type of the elements in the output list (integers) is different from the type of the elements of the input list (strings). This happens frequently in functional programming. It is not uncommon to move a collection of inputs through a series of transformations, changing the type with each transformation.

Sets

The Set class(es) also defines a map method. It behaves essentially the same as the map methods for lists, arrays, or sequences, but there are some minor differences. Sets are not sequential collections, so the order of their elements is not guaranteed. In other words, if you iterate through the input set and get the elements out in a certain order, after you map over it to get the output set, iterating over the elements of the output set may not yield the same order as the counterpart elements in the input set. Furthermore, by definition, sets cannot contain duplicates. So then, what happens if you map a function over the input set that produces duplicate output values? Let’s see:

val lengths = Set("four", "five", "six", "seven") map {
  _.length
}
// lengths: scala.collection.immutable.Set[Int] = Set(4, 3, 5)

That makes sense. Duplicates are dropped. Well, now you know. And knowing is half the battle. (What’s the other half, though? They never did tell us that!)

Strings

Finally, you can map over strings as well. Mapping over strings operates on each character in the string:

val allcaps = "sesquipedalian" map { _.toUpper }
// allcaps: String = SESQUIPEDALIAN

If you need to transform the characters into values of another type, map can take a function that transforms characters into a different type:

val allcaps = "sesquipedalian".toSeq map { _.toInt }
// allcaps: Seq[Int] = Vector(115, 101, 115, ... )

Scala Saturday – The flatten Method

Collections

All of Scala’s collections define a flatten method that takes a collection of collections and flattens them. That is, it takes the elements of the nested collections, pulls them out, and puts them all together in a new collection. For sequential collections, such as a list, the new collection maintains the order of the elements in the original collections.

To illustrate, let’s say that the heroes of Icewind Dale have to team up with their roguish nemeses to fight a common enemy:

val heroes = List("Drizzt", "Bruenor", "Wulfgar", "Catti-brie")
val rogues = List("Jarlaxle", "Artemis")
val teams = List(heroes, rogues)
// teams: List[List[String]] =
//   List(
//     List(Drizzt, Bruenor, Wulfgar, Catti-brie),
//     List(Jarlaxle, Artemis) )

So now you have a list of lists. To get your temporary alliance, you call flatten on podcasts:

val alliance = teams.flatten
// alliance: List[String] =
//   List(Drizzt, Bruenor, Wulfgar, Catti-brie, Jarlaxle, Artemis)

As you can see, flatten flattens the nested lists into one long list and preserves the order of the elements as they were in the original nested lists.

Strings

Hey, strings are collections, too, right? Collections of characters, so if it should ever suit your needs, you can flatten a collection of strings into a collection of characters:

val atrocious = List(
  "super", "cali", "fragi", "listic", "expi", "ali", "docious")
val precocious = atrocious.flatten
// precocious: List[Char] = List(s, u, p, e, r, c, a, l, i, ...

Of course, more times than not, you probably want to combine those strings into one long string rather than a sequence of characters. That’s where mkString comes in. It concatenates a collection of strings together, delimited by whatever separator you prescribe. (Some languages call this operation “join.”) Like so:

val delimited = List("lock", "stock", "barrel") mkString ", "
// delimited: String = lock, stock, barrel

And if you just want to jam the strings all together with no delimiter, just use an empty string as the delimiter:

val precocious = atrocious mkString ""
// precocious: String = supercalifragilisticexpialidocious

Scala Saturday – Infix Notation

I have been using infix notation already in previous Scala Saturday posts, but I decided that it deserves some comment in its own right.

Perhaps you need to do some math with complex numbers, so you whip yourself up a quick Complex case class with a plus method to perform addition:

case class Complex(re: Double, im: Double) {
  def plus(other: Complex): Complex =
    Complex(re + other.re, im + other.im)
}

No sweat. Now you can add a couple of complex numbers:

val a = Complex(2, 5)
val b = Complex(1, -3)
val c = a.plus(b)
// c: Complex = Complex(3.0,2.0)

Wouldn’t it be great, though, if we could just write this?

val c = a plus b

The Fix Is In

If this were Java, we’d be AOL. But good news! Scala allows for infix notation. Martin Odersky in Scala by Example puts it this way:

The binary operation E op E′ is always interpreted as the method call E.op(E′).

In other words, you can write this:

val x = obj.method(arg)

… like this:

val x = obj method arg

So, in fact, the following works like a charm:

val a = Complex(2, 5)
val b = Complex(1, -3)
val c = a plus b
// c: Complex = Complex(3.0,2.0)

That’s why you can create a Range this way:

val r = 1 to 7
// r: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7)

The Scala library has defined an extension method to for Ints that constructs a Range from a beginning Int to an ending Int.

Smooth Operators

This option of writing method calls in the form of binary operations allows you, as you’ve seen already, to ditch some clutter, but it also lets you write in more expressive mathematical terms when appropriate. We add real numbers and concatenate strings with a + sign. Why shouldn’t we do the same with complex numbers?

Now technically speaking, Scala does not provide operator overloading. It does something arguably better: It lets you name a method almost anything you want—a number of mathematical operators included! So then, let’s define a + method:

case class Complex(re: Double, im: Double) {
  def plus(other: Complex): Complex =
    Complex(re + other.re, im + other.im)
  def +(other: Complex) = plus(other)
}

Now you could write this:

val a = Complex(2, 5)
val b = Complex(1, -3)
val c = a.+(b)
// c: Complex = Complex(3.0,2.0)

But (in my best Graham Chapman) that’s just silly! The infix option allows you to write an addition operation naturally as you’ve been doing since grade school:

val c = a + b
// c: Complex = Complex(3.0,2.0)

And you can even chain operations:

val x = Complex(1,1) + Complex(1,2) + 
  Complex(2,3) + Complex(3,5)
// x: Complex = Complex(7.0,11.0)

Without infix notation, you’d be stuck with this:

val x = Complex(1,1).
          plus(Complex(1,2)).
          plus(Complex(2,3)).
          plus(Complex(3,5))
// x: Complex = Complex(7.0,11.0)

… which is not terrible, but isn’t the infix notation a marked improvement?

Again, there’s nothing special about the + sign or any other mathematical symbol: It’s just a method. You can chain other methods calls together in an infix fashion, too. Looking at Ranges one more time, you can create a Range with a custom step size by chaining the to method with the by method:

val threes = 0 to 20 by 3
// threes: scala.collection.immutable.Range = Range(0, 3, 6, 9, 12, 15, 18)

Parenthetically Speaking

Infix notation also works when a method takes more than one input, but you do have to group the arguments with parentheses:

// Oops!
val s = "abc" substring 2,3
// error: ';' expected but integer literal found.
//       "abc" substring 2,3
//                        ^

// Ah, this is better
val s = "abc" substring (2,3)
// s: String = c

Also if the method takes multiple parameter sets, you can use infix notation for the first argument, but you must wrap that infix expression in parentheses in order to apply the second argument. Take foldLeft, for example:

val f = (1 to 7 foldLeft 5)(_*_)
// f: Int = 25200

That’s not bad, but I suppose it’s a matter of preference whether it is any better than the postfix version:

val f = (1 to 7).foldLeft(5)(_*_)
// f: Int = 25200

Scala Saturday – The contains and containsSlice Methods

Scala defines the contains method on all its collection classes.

Sets

Let’s say you have a set containing the nicknames of the guys in the band Rush, and you want to see whether a certain person is in the band. Just use contains:

val rush = Set("Dirk", "Lerxst", "Pratt")

val gotAlex = rush contains "Lerxst"
// gotAlex: Boolean = true
val gotAngus = rush contains "Angus"
// gotAngus: Boolean = false

Arrays, Lists, and Vectors (Oh, My!)

Seq and the sequence-like classes all define the contains method:

val rushSeq = Seq("Dirk", "Lerxst", "Pratt")
val rushA = Array("Dirk", "Lerxst", "Pratt")
val rushL = List("Dirk", "Lerxst", "Pratt")
val rushV = Vector("Dirk", "Lerxst", "Pratt")

val gotGene = rushSeq contains "Gene"
// gotGene: Boolean = false
val gotNeil = rushA contains "Pratt"
// gotNeil : Boolean = true
val gotAlex = rushL contains "Lerxst"
// gotAlex : Boolean = true
val gotPeter = rushV contains "Peter"
// gotPeter : Boolean = false

Sequences and the like also define a containsSlice method that takes a sequence and checks it is found in the collection. Order does matter, though:

val gotGedAndAl = rushSeq containsSlice Seq("Dirk", "Lerxst")
// gotGedAndAl: Boolean = true

// rushSeq has "Dirk" before "Lerxst", so the following fails
val gotAlAndGed = rushSeq containsSlice Seq("Lerxst", "Dirk")
// gotAlAndGed: Boolean = false

Strings

Because Scala just uses Java’s String class, we inherit the following from Java:

val gotX = "Lerxst" contains 'x'
// gotX : Boolean = true
val gotA = "Lerxst" contains 'A'
// gotA : Boolean = false

Java overloads contains to accept a String as well as a char:

val gotXs = "Lerxst" contains "xs"
// gotXs : Boolean = true
val gotAb = "Lerxst" contains "Ab"
// gotAb : Boolean = false

Because a String is also a sequence, Scala extends String with containsSlice if you prefer (perhaps for idiomatic reasons) to use it instead of contains. It can take a String or a sequence of Chars:

val gotXs = "Lerxst" containsSlice "xs"
// gotXs : Boolean = true
val gotXs = "Lerxst" containsSlice Seq('x', 's')
// gotXs : Boolean = true

Maps

Map has a contains method, and it operates on the Map‘s keys, not its values or its key-value pairs:

val rushM = Map(
  "Dirk" -> "bass",
  "Lerxst" -> "guitar",
  "Pratt" -> "drums")
val gotGed = rushM contains "Dirk"
// val gotGed : bool = true

To look among a Map‘s values, get the valuesIterator first. I’m not wild about this interface. It should have been values instead, but values returns an Iterable, which has no contains method:

val gotDrummer = rushM.valuesIterator contains "drums"
// gotDrummer: Boolean = true
val gotSitarist = rushM.valuesIterator contains "sitar"
// gotSitarist: Boolean = false

There is no contains variant that searches on a key-value pair. You can define an extension method containsPair:

object MapExt {
  implicit class RichMap[A,B](val m: Map[A,B]) extends AnyVal {
    def containsPair(p: (A,B)): Boolean =
      m get p._1 contains p._2
  }
}

import MapExt._

val gotGedOnBass = rushM containsPair ("Dirk" -> "bass")
// gotGedOnBass: Boolean = true

val gotGedOnFlute = rushM containsPair ("Dirk" -> "flute")
// gotGedOnFlute: Boolean = false

val gotGeorgeOnSitar = rushM containsPair ("George" -> "sitar")
// gotGeorgeOnSitar: Boolean = false

Scala Saturday — The exists Method

The Scala collections API defines the exists method on all its classes. The exists method takes a predicate. If any item in the collection meets the predicate, exists returns true. Otherwise, it returns false.

List(1, 3, 5, 7, 9) exists { _ == 3 }
// res0: Boolean = true

(1 to 12) exists { _ < 1 }
// res1: Boolean = false

Array(1, 3, 5, 7, 9) exists { _ % 3 == 0 }
// res2: Boolean = true

Set(1, 3, 5, 7, 9) exists { _ % 2 == 0 }
// res3: Boolean = false

Strings are collections/sequences of characters, so both of the following are valid:

"asdf" exists { _ == 's' }
// res4: Boolean = true

"asdf" exists { _ == 'b' }
// res5: Boolean = false

The Map module also defines an exists function, but the predicate takes two inputs: one for the key and one for the value.

val m = Map("A" -> 1, "B" -> 2, "C" -> 4, "D" -> 8)
m exists { _ == "D" -> 8 }
// res6: Boolean = true

And of course, if you want to test only the key or only the value, only test the first field or the second field, respectively, of the input tuple.

m exists { _._1 == "Z" }
// res7: Boolean = false

m exists { _._2 == 2 }
// res8: Boolean = true

Option also defines an exists method. If the option is Some, then exists applies the predicate to the value in the Some instance and returns the result. If the option is None, then exists always returns false (as the REPL is kind enough to point out to us).

Some("X") exists { _ == "X" }
// res9: Boolean = true

Some("X") exists { _ == "Y" }
// res10: Boolean = false

None exists { _ == "X" }
// <console>:8: warning:
// comparing values of types
// Nothing and String using
// `==' will always yield false
//      None exists { _ == "X" }
//                      ^
// res11: Boolean = false

F# Fridays and Scala Saturdays

I’ve decided to take a page out of Steven Proctor‘s book–well, blog actually. He has been doing Ruby Tuesday, a weekly series of posts on Ruby. Each week he examines a certain function in the language. He also publishes a counterpart series, Erlang Thursday, in which he does the same thing in—you guessed it—APL.

Kidding. It’s Erlang, of course.

The nice thing about these series is that Proctor examines the same or similar features in both languages. You get to see the application of a concept in Ruby, and then a couple of days later, the same concept in Erlang. It’s interesting to note the similarities and differences in the two languages.

In like fashion, I am beginning two series: F# Friday and Scala Saturday. I’ll probably borrow (i.e., steal blind) a number of Proctor’s ideas, that is, cover the same sort of functions he has. I thank Mr. Proctor for the idea and Mrs. Ezell, my middle school grammar teacher, for alliteration.