While I was going through the Scala collections API, I found the mere reading through the method and its functionality is not going to help. And I also realised that it will be useful if I can document them. And with that idea, I will try to document the APIs which I have tried. Please feel free to suggest more examples and also corrections where ever necessary. I would be using a List for all the methods.
also read:
In all the subsequent examples I would be using the REPL (Read Evaluate Print Loop) tool for running the commands which can be invoked by running the scala command from the command line.
map, foreach, flatMap, collect:
defining a List:
scala> val numbers = List(2,3,4,5,6,7,23,42,34) numbers: List[Int] = List(2, 3, 4, 5, 6, 7, 23, 42, 34)
All the collection classes in Scala extend the Iterable trait and right through this post we will keep that API doc page open as we would repeatedly refer to it.
foreach: This methods iterates over the elements in the collection and applies the function passed to the foreach method. There’s another version of foreach method in which the function passed can return a value. But effectively the return value of the function is neglected. The function should be of form: (param) => {block} (we can make this more concise, I will show in the example)
scala> numbers.foreach((x) => {print(x+" ")}) 2 3 4 5 6 7 23 42 34 scala> numbers.foreach(x => print(x+" ")) 2 3 4 5 6 7 23 42 34
You use this when you want to iterate over the collection and perform some operation on the elements.
map: This is iterates over the collection but is different from foreach in the way that this generates a new collection from the existing collection by applying the passed function to each element of the collection. Suppose I want to create a List with elements twice the original elements:
scala> numbers.map((x)=> x+2) res69: List[Int] = List(4, 5, 6, 7, 8, 9, 25, 44, 36) scala> numbers res70: List[Int] = List(2, 3, 4, 5, 6, 7, 23, 42, 34)
In the Scala Doc the map method is declared as:
map[B](f: (A) ⇒ B): Iterable[B]
and it clearly says that it accepts a function which takes in one parameter(the type of which is the type of the elements of the list) and returns another value whose type can be different. And map method overall returns an Iterable which contains elements whose type is the type of the return values of the function passed. Confusing? Lets look at another example. Our original list is list of Int, but we will generate another list of Double values:
scala> numbers.map(_ + 1.5) res71: List[Double] = List(3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 24.5, 43.5, 35.5)
“_” represents the parameter value passed to the function which in this case is an Int. But map method returns a list of Double and our function passed also returns Double value. In all these above operations, the original list didn’t get modified.
flatMap: There’s a slight twist in this method. The Scala doc declares this method as: flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): Iterable[B]. This does the same task as that of map method- create a new list by applying the function passed on each element of the list. But the function passed to map would return just one value, but in the flatMap method, the function passed should return an instance of type GenTraversableOnce which contains some elements of type B. So the overall return type of the flatMap is a Iterable[B] which is nothing but a collection which contains elements of type B. Time for a quick example:
//Acts like a map scala> numbers.flatMap(x => List(x+2)) res73: List[Int] = List(4, 5, 6, 7, 8, 9, 25, 44, 36) scala> numbers res74: List[Int] = List(2, 3, 4, 5, 6, 7, 23, 42, 34) scala> numbers.flatMap(x => List(x,x+2)) res75: List[Int] = List(2, 4, 3, 5, 4, 6, 5, 7, 6, 8, 7, 9, 23, 25, 42, 44, 34, 36)
in the 2nd use of flatMap method we pass a function which returns a List of 2 elements for each element in the numbers list, which means the resultant list would have 2 elements for each element in the original list = twice the number of elements in the “numbers” list. We can return a List instance from the function passed to the map method, but the map method doesn’t flatten the return values to form one collection, the below example would clear this:
scala> numbers.map(x => List(x,x+2)) res84: List[List[Int]] = List(List(2, 4), List(3, 5), List(4, 6), List(5, 7), List(6, 8), List(7, 9), List(23, 25), List(42, 44), List(34, 36))
so you see that in case of flatMap, it flattens the each returned list into one common List, but the map method creates a List of Lists.
collect: This took some time for me to understand. One has to know about Partial Functions before diving into understanding this method. Partial Functions are different from the partially applied functions. Partial Functions are those functions which are defined for certain parameter values from within the range for the given type i.e if the Parameter for the PartialFunction is Int, then it can be defined for few values from the range of values an Int can take.
scala> val partial1:PartialFunction[Int,Int] = { case 1 => 12} partial1: PartialFunction[Int,Int] = <function1> scala> partial1.isDefinedAt(1) res76: Boolean = true scala> partial1.isDefinedAt(2) res77: Boolean = false
the above partial function is defined only for 1. Partial functions can be combined using orElse. For more details on Partial Functions, I would recommend the examples here.
The collect method applies the partial function on each element of the list and constructs a new collection from the return values on application of partial function. An example for this would be to return words representation if the element of the list is 1/2/3.
scala> val oneTwoThree:PartialFunction[Int,String] = {case x if x == 1 => "One" | case x if x == 2 => "Two" | case x if x == 3 => "Three" | } oneTwoThree: PartialFunction[Int,String] = <function1> scala> numbers res78: List[Int] = List(2, 3, 4, 5, 6, 7, 23, 42, 34) scala> numbers.collect(oneTwoThree) res79: List[String] = List(Two, Three)
One can see that on applying the partial function using the collect method the resultant is a List[String].
Lets look at another example to just get the even numbers from the list:
scala> numbers res80: List[Int] = List(2, 3, 4, 5, 6, 7, 23, 42, 34) scala> val isEven:PartialFunction[Int,Int] = { case x if x % 2 == 0 => x} isEven: PartialFunction[Int,Int] = <function1> scala> numbers.collect(isEven) res81: List[Int] = List(2, 4, 6, 42, 34)
One might wonder that the same can be achieved using map, but in cases where the elements dont satisfy the condition we get some empty value, but in collect we dont get any output if the element doesn’t satisfy the condition. For example, let me just use map method to implement the above:
scala> numbers.map((x)=> if ( x % 2 == 0) x) res82: List[AnyVal] = List(2, (), 4, (), 6, (), (), 42, 34)
one can see that in cases where the elements of the collection didn’t satisfy the condition there was an empty value returned.
These are just 4 of the many available methods for collections in Scala. In future posts I will show some examples with the other methods. Now that you have got an idea of how to understand the documentation and use these methods, rest of the method should be easy to understand.