0% found this document useful (0 votes)
17 views

Introduction To Data Science Programming Handout Set 2

This document discusses data science programming tools, specifically Scala and Apache Spark. It provides an introduction to these tools, explaining what they are used for, their advantages over other tools for big data analysis, and basic concepts like classes and objects in Scala.

Uploaded by

dinoda6464
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views

Introduction To Data Science Programming Handout Set 2

This document discusses data science programming tools, specifically Scala and Apache Spark. It provides an introduction to these tools, explaining what they are used for, their advantages over other tools for big data analysis, and basic concepts like classes and objects in Scala.

Uploaded by

dinoda6464
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 52

1

Data Science Programming Tools


This course unit introduces the learners to the world of programming tools that are used extensively in
big data analysis.
Big data is there and what is possible with big data can only be realized if we can be able to dig into the
ocean of big data.
Remember catch phrase: “Big data is gold a waiting to be mined”.
Traditional programming tools, C, C++, Fortrain, Java as up to Java 6, have had their share in software
development and has contributed immensely in automating business processes. However in their
current state they might not able to provide the power needed that is needed to derive the benefits of
big data.
This is where Scala and Hadoop and other tools such as Apache Spark come in.
Specifically we will focus on Scala and Apache Spark.
Note
1. Scala runs on Java Virtual Machine ( JVM) so java must be installed in your machine.
2. Spark is Hadoop’s sub-project. Therefore, it is better to install Spark into a Linux based system.
3. Practically Spark is not a modified version of Hadoop and is not dependent on Hadoop
because it has its own cluster management. Hadoop is just one of the ways to implement Spark.
4. Apache spark runs on scala so that so scala must be installed as well as java .
5. Apache Spark is an in-memory big data platform that performs especially well with iterative
algorithms providing
◦ 10-100x speedup over Hadoop with some algorithms, especially iterative ones as found
in machine learning
◦ Originally developed by UC Berkeley starting in 2009 and later Moved to an Apache
project in 2013
◦ Spark itself is written in Scala, and Spark jobs can be written in Scala, Python, and Java
(and more recently R and SparkSQL)
◦ Other libraries may include Streaming, Machine Learning, Graph Processing etc.
◦ Percent of Spark programmers who use each language distribution as as 88% Scala,
44% Python, 22% Java
◦ For data scientist who deals with large datasets, Scala will be invaluable. It's practically
2

the de facto language for the current Big Data tools like Apache Spark, Finagle,
Scalding, etc.
Many of the high performance data science frameworks that are built on top of Hadoop usually are
written and use Scala or Java. The reason Scala is used in these environments is because of its amazing
concurrency support, which is key in parallelizing a lot of the processing needed for large data sets. It
also runs on the JVM, which makes it almost a no-brainer when paired with Hadoop.

Introduction To Scala Programming


• The name Scala stands for “scalable language.” It is so named because it was designed to grow
as the demands of its users grows.
• With Scala you can a wide range of programming tasks, from writing small scripts( as in
JavaScript or bash scripting) to building large systems.
• Scala is a modern multi-paradigm programming language designed to express common
programming patterns in a concise, elegant, and type-safe way.
• Scala has been created by Martin Odersky and he released the first version in 2003.
• Scala is a hybrid programming language. It smoothly integrates the features of object-oriented
and functional languages.
• Scala is compiled to run on the Java Virtual Machine.
• You can think of Scala as fist choice programming platform to boost your development
productivity, to write application scalability and systems that has overall reliability.

Prerequisites
• Scala Programming is based on Java, so if you are aware of Java syntax, then it's pretty easy to
learn Scala.
• Further if you do not have expertise in Java but if you know any other programming language
like C, C++ or Python then it will also help in grasping Scala concepts very quickly.
• It is also very important that you have done CMT 203: Introduction to system Administration.
Indeed almost all big data analysis tools and concepts are largely based on open source
solutions than proprietary solutions.
3

Scala - Classes & Objects


Scala is an object oriented programming language. Classes and objects are at the heart of OOP.
A class is a user-defined blueprint or prototype from which objects are created.
As a blueprint for objects you can use it to create(instantiate) as many objects as required by the
application with the keyword new.
Basically, a class combines the data fields and methods(member function which defines actions) into a
single unit.
Basically, in a class a constructor is used for initializing new objects, fields are variables that provide
the state of the class and its objects, and methods are used to implement the behavior of the class and
its objects.
Through the object you can use all functionalities ( also known as behavior) of the defined class.

Objects
It is a basic unit of Object Oriented Programming and represents the real-life entities. A typical Scala
program creates many objects, which as you know, interact by invoking methods.
The process of invoking methods is refereed to as message passing.
An object consists of :
• State: It is represented by attributes of an object. It also reflects the properties of an object.
• Behavior: It is represented by methods of an object. It also reflects the response of an object
with other objects.
• Identity: It gives a unique name to an object and enables one object to interact with other
objects.
Student
The following diagram demonstrates
the class and object by taking an Data Members
example of class Student, which Behavior
contains the member variables (name (Methods)
and studentno) and member methods
(setName() and setStNo()) which are
the members of the class.
In the following diagram, Student is a
class and Carol, Ojwang, Marie and
Carol Achieng Ojwang Marie
Achieng are the objects of Student
10231290 10231789 10231456 10231478
class, those are having name and stno.
4

Basics of a Class In Scala


Following is a simple syntax to define a basic class in Scala. This class defines two variables x and y
and a method: move, which does not return a value.
Interestingly in Scala the class name works as a class constructor which can take a number of
parameters.

Basic class constructor


A Scala class whose constructor defines two parameters, firstName and lastName:
class Person(var firstName: String, var lastName: String)

Instantiating new Person instances like this:


val p = new Person("Bill", "Panner")

Defining parameters in a class constructor automatically creates fields in the class, and in this
example you can access the firstName and lastName fields like this:
println(p.firstName + " " + p.lastName)
Bill Panner

Since the fields are defined as var fields, they’re also mutable, meaning they can be changed. This is
how you change them:
scala> p.firstName = "William"
p.firstName: String = William

scala> p.lastName = "Bernheim"


p.lastName: String = Bernheim

The Scala class code above is roughly the equivalent of this Java code: :
public class Person {
private String firstName;
private String lastName;
//Constructors
public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
//getters and setters methods
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
5

}
public String getLastName() {
return this.lastName;
}

public void setLastName(String lastName) {


this.lastName = lastName;
}

val vs var
In the example above both fields were defined as var fields which makes them mutable.

You can also define them as val fields, which makes them immutable:
class Person(val firstName: String, val lastName: String)
If you now try to change the first or last name of a Person instance, you’ll see an
error:

scala> p.firstName = "Fred"


<console>:12: error: reassignment to val
p.firstName = "Fred"
^

scala> p.lastName = "Jones"


<console>:12: error: reassignment to val
p.lastName = "Jones"
^

Tip: If you use Scala to write OOP code, create your fields as var fields so you can mutate them.

Class constructors
In Scala, the primary constructor of a class is a combination of:
i. The constructor parameters
ii. Methods that are called in the body of the class
iii. Statements and expressions that are executed in the body of the class
NOTE
Fields declared in the body of a Scala class are handled in a manner similar to Java; they’re assigned
when the class first instantiates ( I.e creates an object)
This Person class demonstrates several of the things you can do inside the body of a class:

Type the code in the scala terminal


6

class Person(var firstName: String, var lastName: String) {


println("the constructor begins")
// 'public' access by default
var age = 0
// some class fields
private val HOME = System.getProperty("user.home")
// some methods
override def toString(): String = s"$firstName $lastName is $age years old"

def printHome(): Unit = println(s"HOME = $HOME")


def printFullName(): Unit = println(this)

printHome()
printFullName()
println("you've reached the end of the constructor")

We use the Scala REPL demonstrates how this class works:


scala> val p = new Person("John", "Nyongesa")
the constructor begins
HOME = /root
John Nyongesa is 0 years old
you've reached the end of the constructor
p: Person = John Nyongesa is 0 years old

What is the age?


scala> p.age
res0: Int = 0

Now setting the age?


scala> p.age = 36
p.age: Int = 36

scala> p
res1: Person = John Nyongesa is 36 years old

scala> p.printHome()
HOME = /root

scala> p.printFullName()
John Nyongesa is 36 years old

When you come to Scala from a more verbose language this constructor approach might feel a little
unusual at first, but once you understand and write a couple of classes with it, you’ll find it to be logical
and convenient and you will like it.
7

Other Scala class examples


Before we move on, here are a few other examples of Scala classes:
1. class Marks (var cat1: Int, var cat2: Int,var exam: Int)
2. class Student(var name: String, var stnum: int)

// A team in a soccer tournament


3. class Team(val name: String, val country: String, players:
Array[String])

4. class Address (
var street: String,
var hsenum: byte,
var city: String,
var state: String
)

Example 1: Working with a class


import java.io.
class Point(xc: Int, yc: Int) {
var x: Int = xc
var y: Int = yc

def move(dx: Int, dy: Int) ={


x = x + dx
y = y + dy
println ("Point x location : " + x);
println ("Point y location : " + y);
}
}

//The driver class

object PointsExample {
def main(args: Array[String]) ={
val pt = new Point(10, 20); //Instantiating an object
// Move to a new location
pt.move(10, 10);
}
}

What is its output?

Supplying Default Values for Constructor Parameters


Scala lets you supply default values for constructor parameters.
Example 2: A general constructor
8

class Marks(var CAT1: Int, var CAT2: Int, var EXAM: Int) {
var sum=CAT1+CAT2+EXAM
override def toString = s"Cat1: $CAT1, Cat2: $CAT2, Exam: $EXAM,
Total: $sum"
}
This can be improved by supplying default values for the parameters:
Example 3: Using a default constructor
class Marks(var CAT1: Int = 10, var CAT2: Int = 15, var EXAM: Int=45) {
var sum=CAT1+CAT2+EXAM
override def toString = s"Cat1: $CAT1, Cat2: $CAT2, Exam: $EXAM,
Total: $sum"
}
By supplying default values for the parameters, you can now create a new mark object in a variety
of different ways:
• new Marks()// Using the default values
• new Marks(5,9) // Only the exam is default.
• new Marks(5,9,35)// The default values are overriden

Here’s what those examples look like in the REPL:


scala> new Marks()
val res8: Marks = Cat1: 10, Cat2: 15, Exam: 45, Total: 70

scala> new Marks(5,9)


val res8: Marks = Cat1:5, Cat2: 9, Exam: 45, Total: 59

scala> new Marks(8,19,35)


val res8: Marks = Cat1: 7, Cat2: 18, Exam: 35, Total: 60

Extending a Class: Inheritance


Scala support inheritance in the same way you do it in Java (use extends key word), but there are two
restrictions:
1. Method overriding requires the override keyword, and
2. Only the primary constructor can pass parameters to the base constructor.
Let us extend our above class and add one more class method.

Inheritance Example 1
Consider Point class as discussed above and class Location that inherit class Point using extends
keyword. By using the extend keyword , the class Location and class Point has an inheritance
9

relationship.
In Such a relationship there are two effects:
1. It makes Location class inherit all non-private members from Point class, and
2. It makes the type Location a subtype of the type Point class.
As usual the Point class is called superclass and the class Location is called subclass.
NOTE: Scala allows inheritance from just one class only.
Note − The move() method in Location class does not override the corresponding definitions of move
since they are different definitions (for example, the former take two arguments while the latter take
three arguments).
Example1: Program to implement inheritance.
import java.io._

class Point(val xc: Int, val yc: Int) {


var x: Int = xc
var y: Int = yc

def move(dx: Int, dy: Int)= {


x = x + dx
y = y + dy
println ("Point x location : " + x);
println ("Point y location : " + y);
}
}

class Location(override val xc: Int, override val yc: Int,


val zc :Int) extends Point(xc, yc){
var z: Int = zc
def move(dx: Int, dy: Int, dz: Int)= {
x = x + dx
y = y + dy
z = z + dz
println ("Point x location : " + x);
println ("Point y location : " + y);
println ("Point z location : " + z);
}
}

object InheritanceExample1 {
def main(args: Array[String])= {
val loc = new Location(10, 20, 15);
// Move to a new location
loc.move(10, 10, 5);
}
}
10

Inheritance Example 2
1. //Class Marks which contains the marks a student can get in a unit
2. class Marks(var CAT1: Int, var CAT2: Int, var EXAM: Int) {
3. val cat1=CAT1;
4. val cat2=CAT1;
5. val exam=EXAM;
6. }
//Class Subject inherits class Marks
7. class Subject(override val CAT1: Int,override val CAT2: Int,override
val EXAM: Int,var code: String,var name: String) extends
Marks(CAT1,CAT2,EXAM){
8. val sname=name;
9. val scode=code;
10. def result() ={
11. var tcat=cat1+cat2
12. var sum=cat1+cat2+exam
13. val grd=if( sum >= 70 )"A" else if( sum >= 60 )"B" else if( sum
>= 50 )"C" else if( sum >= 40 )"D" else "F"
14. print(scode + "\t");
15. //Printing the results
16. print(sname + "\t");
17. print(cat1 + "\t");
18. print(cat2 + "\t");
19. print(tcat + "\t");
20. print(exam + "\t");
21. print(sum + "\t");
22. println(grd);
23. }//End of rsult
24. }//End of Subject class
// The main Class. Take note of the use of scala asInstanceOf[Int] method
to cast the doulble into an Int
25. object InheritanceExample2 {
26. def main(args: Array[String]) ={
27. val c1=scala.math.ceil(Math.random()*10).asInstanceOf[Int]
28. val c2=scala.math.ceil(Math.random()*20).asInstanceOf[Int]
29. val ex=scala.math.ceil(scala.math.random()*70).asInstanceOf[Int]
30. val subj = new Subject(c1, c2, ex,"CMT201","OOP");
31.
32. subj.result();
33. }
34. }

Exercise
1. Modify the InheritanceExample1 class so that the location arguments are generated
randomly such that every execution results to different new location
11

1. Modify the InheritanceExample2 above such that the Mark class contains a method grade()
to get the grade of the sum marks. The result() function should then make use of this method
Marks()

Implicit Classes
Implicit classes allow implicit conversations with class’s primary constructor when the class is in
scope. Implicit class is a class marked with ‘implicit’ keyword. This feature is introduced in Scala 2.10.
Syntax − The following is the syntax for implicit classes. Here implicit class is always in the object
scope where all method definitions are allowed because implicit class cannot be a top level class.

Syntax
object <object name> {
implicit class <class name>(<Variable>: Data type) {
def <method>(): Unit =
}
}

Example
Let us take an example of an implicit class named IntTimes with the method times(). It means the
times () contain a loop transaction that will execute the given statement in number of times that we
give.
Let us assume the given statement is “4 times println (“Hello”)” means the println (“”Hello”) statement
will execute 4 times.
The following is the program for the given example. In this example two object classes are used (Run
and Demo) so that we have to save those two classes in different files with their respective names as
follows.
Run.scala − Save the following program in Run.scala.
object Run {
implicit class IntTimes(x: Int) {
def times[A](f: =>A): Unit = {//times[A] can be of any type or no type at all
def loop(current: Int): Unit =
if(current > 0){
f
loop(current - 1)
}
loop(x)
}
}
}

Demo.scala − Save the following program in Demo.scala.


12

import Run._
object Demo {
def main(args: Array[String]) {
4 times println("hello")
}
}

Note −
• Implicit classes must be defined inside another class/object/trait (not in top level).
• Implicit classes may only take one non –implicit argument in their constructor.
• Implicit classes may not be any method, member or object in scope with the same name as the
implicit class.

Singleton Objects
Scala support singleton objects. A singleton is a class that can have only one instance, i.e., one Object.
You create singleton using the keyword object instead of class keyword. Since you can't instantiate a
singleton object, you can't pass parameters to the primary constructor.
You have been using singleton objects all along in all the objects where you have used the Scala's main
method.
Following is the same example program to implement singleton.

SingletonExample
import java.io._
class Point(val xc: Int, val yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
}
}

object PointMain{
def main(args: Array[String]) {
val point = new Point(10, 20)
printPoint
def printPoint{
println ("Point x location : " + point.x);
println ("Point y location : " + point.y);
}
13

}
}

Scala - Access Modifiers


Access modifiers restrict accesses to the Members of packages, classes or objects to certain regions of
code . The regions can be labeled with the access modifiers private and protected, and if we are not
using either of these two keywords, then access will be assumed as public. To use an access modifier,
you include its keyword in the definition of members of package, class or object.

Private Members
A private member is visible only inside the class or object that contains the member definition.
Following is the example code snippet to explain Private member −

Example
class Outer {
class Inner {
private def go() { println("go") }
class InnerMost {
go()
}
}
(new Inner).go()
}

In Scala, the access (new Inner). go() is illegal because go is declared private in Inner class and the
access is not from within class Inner but Outer
The first access to go in class Innermost is OK, because that access is contained in the body of class
Inner.
Java would permit both accesses because it lets an outer class access private members of its inner
classes.

Protected Members
A protected member is only accessible from subclasses of the class in which the member is defined.
Following is the example code snippet to explain protected member −

Example
package p {
class Super {
14

protected def go() { println("Am going") }


}

class Sub extends Super {


go() // OK
}

class AnyOther {
(new Super).go() // Error: f is not accessible
}
}

The access to go in class Sub is OK because f is declared protected in ‘Super’ class and ‘Sub’ class is a
subclass of Super. The access to go() in ‘AnyOther’ class is not permitted, because class ‘AnyOther’
does not inherit from class ‘Super’.
In Java, the latter access would be still permitted because ‘AnyOther’ class is in the same package as
‘Sub’ class.

Public Members
Unlike private and protected members, it is not required to specify Public keyword for Public members.
There is no explicit modifier for public members. Such members can be accessed from anywhere.
Following is the example code snippet to explain public member −

Example
class Outer {
class Inner {
def go() { println("Am going") }

class InnerMost {
go() // OK
}
}
(new Inner).go () // OK because now f() is public
}

Scope of Protection
Access modifiers in Scala can be augmented with qualifiers. A modifier of the form private[X] or
protected[X] means that access is private or protected "up to" X, where X designates some enclosing
package, class or singleton object.
Consider the following example −
15

Example
package society {
package professional {
class Executive {
private[professional] var workDetails = null
private[society] var friends = null
private[this] var secrets = null
def help(another : Executive) {
println(another.workDetails)
println(another.secrets) //ERROR
}
}
}
}

Note − the following points from the above example −


• Variable workDetails will be accessible to any class within the enclosing package professional.
• Variable friends will be accessible to any class within the enclosing package society.
• Variable secrets will be accessible only on the implicit object within instance methods (this).

Scala - Traits
• Scala traits are one of the great features it provides. A trait can be used just like a Java
interface, and also as an abstract classes that have real methods.
◦ Remember in Java interfaces have only abstract methods that are not explicitly abstract,
and an abstract class must have at least one explicit method.
• Scala classes can also extends and “mix in” multiple traits.
• Scala also has the concept of an abstract class, and we’ll discuss when you should use an
abstract class instead of a trait.
• A trait encapsulates methods and field definitions, which can then be reused by mixing them
into classes.
Remember in class inheritance,a class must inherit from just one superclass in scala,
• In Traits a class can mix in any number of traits.
• Traits are used to define object types by specifying the signature of the supported methods.
• Scala also allows traits to be partially implemented but traits may not have constructor
parameters.
• A trait definition looks just like a class definition except that it uses the keyword trait.
16

Using Scala Traits as Interfaces


One way to use a Scala trait is like the original Java interface, where you define the desired
interface for some piece of functionality, but you don’t implement any behavior.

Example
Imagine that you want to write some code to model animals like dogs and cats, any animal that has a
tail. In Scala you write a trait to start that modeling process like this:
trait TailWagger {
def startTail(): Unit
def stopTail(): Unit
}

That code declares a trait named TailWagger that states that any class that extends TailWagger
should implement startTail and stopTail methods. Both of those methods take no input
parameters and have no return value. This code is equivalent to this Java interface:
public interface TailWagger {
public void startTail();
public void stopTail();
}

Extending a trait
Given this trait TailWagger

You can write a class that extends the trait and implements those methods like this:
class Dog extends TailWagger {
// the implemented methods
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
}

You can also write those methods like this, if you prefer:
class Dog extends TailWagger {
def startTail() = println("tail is wagging")
def stopTail() = println("tail is stopped")
}

Notice that in either case, you use the extends keyword to create a class that extends a single trait:

If you paste the TailWagger trait and Dog class into the Scala REPL, you can test the code like this:
scala> val d = new Dog
17

d: Dog = Dog@234e9716

scala> d.startTail
tail is wagging

scala> d.stopTail
tail is stopped

Extending multiple traits : Using extends & with


Scala lets you create very modular code with traits. For example, you can break down the attributes of
animals into small, logical, modular units:
trait Speaker {
def speak(): String
}

trait TailWagger {
def startTail(): Unit
def stopTail(): Unit
}

trait Runner {
def startRunning(): Unit
def stopRunning(): Unit
}

Once you have those small pieces, you can create a Dog class by extending all of them, and
implementing the necessary methods:
class Dog extends Speaker with TailWagger with Runner {

// Speaker
def speak(): String = "Woof!"

// TailWagger
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")

// Runner
def startRunning(): Unit = println("I'm running")
def stopRunning(): Unit = println("Stopped running")

Notice how extends and with are used to create a class from multiple traits:

We use with to extend subsequent traits after Speaker


18

Using Scala Traits as Abstract


To demonstrate this, here’s a Scala trait that has a concrete method named speak, and an abstract
method named comeToMaster:
trait Pet {
def speak = println("Yo") // concrete implementation of a speak method
def comeToMaster(): Unit // abstract- No implemention
}

When a class extends a trait, each abstract method must be implemented, so here’s a class that extends
Pet and defines comeToMaster:
class Dog(name: String) extends Pet {
def comeToMaster(): Unit = println("Whoo-whoo, I'm coming Sir!")
}

Unless you want to override speak, there’s no need to redefine it, so this is a perfectly complete Scala
class.
Now you can create a new Dog like this:
val d = new Dog("Boom")

Then you can call speak and comeToMaster. This is what it looks like in the REPL:
scala> val d = new Dog("Boomi")
d: Dog = Dog@4136cb25

scala> d.speak
Yo

scala> d.comeToMaster
Woo-hoo, I'm coming!

Overriding an implemented method


A class can also override a method that’s defined in a trait. Here’s an example:
class Cat extends Pet {
// override 'speak'
override def speak(): Unit = println("meow")
def comeToMaster(): Unit = println("No not me.")
}

The REPL shows how this works:


scala> val c = new Cat
c: Cat = Cat@1953f27f
19

scala> c.speak
meow

scala> c.comeToMaster
No not me

Mixing in multiple traits that have behaviors


A great thing about Scala traits is that you can mix multiple traits that have behaviors into classes. For
example, here’s a combination of traits, one of which defines an abstract method, and the others that
define concrete method implementations:
trait Speaker {
def speak(): String //abstract
}

trait TailWagger {
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
}

trait Runner {
def startRunning(): Unit = println("I'm running")
def stopRunning(): Unit = println("Stopped running")
}

Now you can create a Dog class that extends all of those traits while providing behavior for the speak
method:
class Dog(name: String) extends Speaker with TailWagger with Runner {
def speak(): String = "Woof!"
}

And here’s a Cat class:


class Cat extends Speaker with TailWagger with Runner {
def speak(): String = "Meow"
override def startRunning(): Unit = println("Yeah ... I don't run")
override def stopRunning(): Unit = println("No need to stop")
}

The REPL shows that this all works like you’d expect it to work. First, a Dog:

scala> d.speak
res0: String = Woof!

scala> d.startRunning
I'm running
20

scala> d.startTail
tail is wagging

Then a Cat:
scala> val c = new Cat
c: Cat = Cat@1b252afa

scala> c.speak
res1: String = Meow

scala> c.startRunning
Yeah ... I don't run

scala> c.startTail
tail is wagging

Mixing traits in on the fly


As a last note, a very interesting thing you can do with traits that have concrete methods is mix them
into classes on the fly. For example, given these traits:
trait TailWagger {
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
}

trait Runner {
def startRunning(): Unit = println("I'm running")
def stopRunning(): Unit = println("Stopped running")
}

and this Dog class:


class Dog(name: String)

you can create a Dog instance that mixes in those traits when you create a Dog instance:
val d = new Dog("Fido") with TailWagger with Runner
---------------------------

Once again the REPL shows that this works:


scala> val d = new Dog("Fido") with TailWagger with Runner
d: Dog with TailWagger with Runner = $anon$1@50c8d274

scala> d.startTail
tail is wagging
21

scala> d.startRunning
I'm running

This example works because all of the methods in the TailWagger and Runner traits are
defined (they’re not abstract).

Abstract Classes
Scala also has a concept of an abstract class that is similar to Java’s abstract class. But because traits are
so powerful, you rarely need to use an abstract class. In fact, you only need to use an abstract class
when:
• You want to create a base class that requires constructor arguments
• Your Scala code will be called from Java code

Scala traits don’t allow constructor parameters


Regarding the first reason, Scala traits don’t allow constructor parameters:
// this won’t compile
trait Animal(name: String)

Therefore, you need to use an abstract class whenever a base behavior must have constructor
parameters:
abstract class Animal(name: String)

However, be aware that a class can extend only one abstract class.

When Scala code will be called from Java code


Regarding the second point — the second time when you’ll need to use an abstract class — because
Java doesn’t know anything about Scala traits, if you want to call your Scala code from Java code,
you’ll need to use an abstract class rather than a trait.

Abstract class syntax


The abstract class syntax is similar to the trait syntax. For example, here’s an abstract class named Pet
that’s similar to the Pet trait we defined in the previous lesson:
abstract class Pet (name: String) {
def speak(): Unit = println("Yo") // concrete implementation
def comeToMaster(): Unit // abstract method
}
22

Given that abstract Pet class, you can define a Dog class like this:
class Dog(name: String) extends Pet(name) {
override def speak() = println("Woof")
def comeToMaster() = println("Here I come!")
}

The REPL shows that this all works as advertised:


scala> val d = new Dog("Rover")
d: Dog = Dog@51f1fe1c

scala> d.speak
Woof

scala> d.comeToMaster
Here I come!

Notice how name was passed along


All of that code is similar to Java, so we won’t explain it in detail. One thing to notice is how the name
constructor parameter is passed from the Dog class constructor to the Pet constructor:
class Dog(name: String) extends Pet(name) {

Remember that Pet is declared to take name as a constructor parameter:


abstract class Pet (name: String) { ...

Therefore, this example shows how to pass the constructor parameter from the Dog class to the Pet
abstract class. You can verify that this works with this code:
abstract class Pet (name: String) {
def speak: Unit = println(s"My name is $name")
}

class Dog(name: String) extends Pet(name)

val d = new Dog("Fido")


d.speak
23

Value classes and Universal Traits


• Value classes are new mechanism in Scala to avoid allocating runtime objects.
• It contains a primary constructor with exactly one val parameter.
• It contains only methods (def) and does not allow var, val, nested classes, traits, or objects.
• Value class cannot be extended by another class ( I.e inherited).
• They are created by extending your value class with AnyVal. The typesafety of custom
datatypes without the runtime overhead.
• Value classes are predefined, they coincide to the primitive kind of Java-like languages. There
are nine predefined value types : Double, Float, Long, Int, Short, Byte, Char, Unit, and
Boolean.
Value classes are mainly used to optimize performance and memory.
Let us take an examples of value classes Weight, Height, Email, Age, etc. For all these examples it is
not required to allocate memory in the application.
A value class is not allowed to extend traits.
Example1: Scala program to illustrate value class

// Creating a value class and extend with AnyVal


class Vcube(val x: Int) extends AnyVal{
// Defining method
def cube() = x*x*x
}

// Using the Vcube to create an object


object ValueClassExample{
// Main method
def main (args: Array[String])= {
// creating the instance of the Value Class
val v = new Vcube(5)
println(v.cube ())
}
}
24

Universal Traits
A universal trait is a trait that extends Any, and only has defs as members, and does no initialization.
Universal traits allow basic inheritance of methods for value classes, but they incur the overhead of
allocation. For example,
To permit value classes to extend traits, universal traits are introduced which extends for Any.

Example
trait Printable extends Any {
def print(): Unit = println(this)
}
class Wrapper(val underlying: Int) extends AnyVal with Printable

object Demo {
def main(args: Array[String]) {
val w = new Wrapper(3)
w.print() // actually requires instantiating a Wrapper instance
}
}

Output
It will give you the hash code of Wrapper class.
Wrapper@13

When to Use Traits?


There is no firm rule, but here are few guidelines to consider −
• If the behavior will not be reused, then make it a concrete class. It is not reusable behavior after
all.
• If it might be reused in multiple, unrelated classes, make it a trait. Only traits can be mixed into
different parts of the class hierarchy.
• If you want to inherit from it in Java code, use an abstract class.
• If you plan to distribute it in compiled form, and you expect outside groups to write classes
inheriting from it, you might lean towards using an abstract class.
• If efficiency is very important, lean towards using a class.
25

Scala - Collections
Scala has a rich set of collection library. Collections are containers of data. Those containers can be
sequenced, linear sets of items like List, Tuple, Option, Map, etc. The collections may have an arbitrary
number of elements or be bounded to zero or one element (e.g., Option).
Collections may be strict or lazy. Lazy collections have elements that may not consume memory until
they are accessed, like Ranges. Additionally, collections may be mutable (the contents of the reference
can change) or immutable (the thing that a reference refers to is never changed). Note that immutable
collections may contain mutable items.
For some problems, mutable collections work better, and for others, immutable collections work better.
When in doubt, it is better to start with an immutable collection and change it later if you need mutable
ones.
This chapter throws light on the most commonly used collection types and most frequently used
operations over those collections.

Collections with Description


ArrayBuffer an indexed, mutable sequence
1 Scala Lists: Scala's List[T] is a linked list of type T.
2 Scala Sets: A set is a collection of pairwise different elements of the same type.
3 Scala Maps: A Map is a collection of key/value pairs. Any value can be retrieved based on its key.
4 Scala Tuples: Unlike an array or list, a tuple can hold objects with different data types.
5 Scala Options: Option[T] provides a container for zero or one element of a given type.
Scala Iterators: An iterator is not a collection, but rather a way to access the elements of a
6
collection one by one.

For every Scala collections classes is described as immutable, it’s safe to assume that the class is
intended for use in a functional programming (FP) style. With these classes you don’t modify the
collection; you apply functional methods to the collection to create a new result. You’ll see what this
means in the examples that follow.
26

The ArrayBuffer Class


The ArrayBuffer class is a mutable sequence, so you can use its methods to modify its contents,
and those methods are similar to methods on Java sequences.
To use an ArrayBuffer you must first import it:
import scala.collection.mutable.ArrayBuffer

After it’s imported into the local scope, you create an empty ArrayBuffer like this:
val ints = ArrayBuffer[Int]()
val names = ArrayBuffer[String]()

Once you have an ArrayBuffer you add elements to it in a variety of ways:


val ints = ArrayBuffer[Int]()
ints += 1
ints += 2

The REPL shows how += works:


scala> ints += 1
res0: ints.type = ArrayBuffer(1)

scala> ints += 2
res1: ints.type = ArrayBuffer(1, 2)

That’s just one way to create an ArrayBuffer and add elements to it. You can also create an
ArrayBuffer with initial elements like this:
val nums = ArrayBuffer(1, 2, 3)

Here are a few ways you can add more elements to this ArrayBuffer:
// add one element
nums += 4

// add multiple elements


nums += 5 += 6

// add multiple elements from another collection


nums ++= List(7, 8, 9)

You remove elements from an ArrayBuffer with the -= and --= methods:
// remove one element
nums -= 9
27

// remove multiple elements


nums -= 7 -= 8

// remove multiple elements using another collection


nums --= Array(5, 6)

Here’s what all of those examples look like in the REPL:


scala> import scala.collection.mutable.ArrayBuffer

scala> val nums = ArrayBuffer(1, 2, 3)


val nums: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)

scala> nums += 4
val res0: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)

scala> nums += 5 += 6
val res1: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 6)

scala> nums ++= List(7, 8, 9)


val res2: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> nums -= 9
val res3: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)

scala> nums -= 7 -= 8
val res4: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 6)

scala> nums --= Array(5, 6)


val res5: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)

More ways to work with ArrayBuffer


As a brief overview, here are several methods you can use with an ArrayBuffer:
val a = ArrayBuffer(1, 2, 3) // ArrayBuffer(1, 2, 3)
a.append(4) // ArrayBuffer(1, 2, 3, 4)
a.append(5, 6) // ArrayBuffer(1, 2, 3, 4, 5, 6)
a.appendAll(Seq(7,8)) // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)
a.clear // ArrayBuffer()

val a = ArrayBuffer(9, 10) // ArrayBuffer(9, 10)


a.insert(0, 8) // ArrayBuffer(8, 9, 10)
a.insertAll(0, Vector(4, 5, 6, 7)) // ArrayBuffer(4, 5, 6, 7, 8, 9, 10)
a.prepend(3) // ArrayBuffer(3, 4, 5, 6, 7, 8, 9,
10)
a.prepend(1, 2) // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8,
9, 10)
a.prependAll(Array(0)) // ArrayBuffer(0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10)
28

val a = ArrayBuffer.range('a', 'h') // ArrayBuffer(a, b, c, d, e, f, g)


a.remove(0) // ArrayBuffer(b, c, d, e, f, g)
a.remove(2, 3) // ArrayBuffer(b, c, g)

val a = ArrayBuffer.range('a', 'h') // ArrayBuffer(a, b, c, d, e, f, g)


a.trimStart(2) // ArrayBuffer(c, d, e, f, g)
a.trimEnd(2) // ArrayBuffer(c, d, e)

Scala - Lists
Scala Lists are quite similar to arrays which means, all the elements of a list have the same type but
there are two important differences.
First, lists are immutable, which means elements of a list cannot be changed by assignment.
Second, lists represent a linked list whereas arrays are flat.
The type of a list that has elements of type T is written as List[T].
Try the following example, here are few lists defined for various data types.
// List of Strings
val fruit: List[String] = List("apples", "oranges", "pears")

// List of Integers
val nums: List[Int] = List(1, 2, 3, 4)

// Empty List.
val empty: List[Nothing] = List()

// Two dimensional list


val dim: List[List[Int]] =
List(
List(1, 0, 0),
List(0, 1, 0),
List(0, 0, 1)
)

All lists can be defined using two fundamental building blocks, a tail Nil and ::, which is pronounced
cons. Nil also represents the empty list. All the above lists can be defined as follows.
// List of Strings
val fruit = "apples" :: ("oranges" :: ("pears" :: Nil))

// List of Integers
val nums = 1 :: (2 :: (3 :: (4 :: Nil)))

// Empty List.
val empty = Nil
29

// Two dimensional list


val dim = (1 :: (0 :: (0 :: Nil))) ::
(0 :: (1 :: (0 :: Nil))) ::
(0 :: (0 :: (1 :: Nil))) :: Nil

Basic Operations on Lists


All operations on lists can be expressed in terms of the following three methods.

Sr.No Methods & Description


1 Head :This method returns the first element of a list.
2 Tail: This method returns a list consisting of all elements except the first.
3 IsEmpty: This method returns true if the list is empty otherwise false.
The following example shows how to use the above methods.

Example
object ListExample1 {
def main(args: Array[String]) {
val fruit = "apples" :: ("oranges" :: ("pears" :: Nil))
val nums = Nil
println( "Head of fruit : " + fruit.head )
println( "Tail of fruit : " + fruit.tail )
println( "Check if fruit is empty : " + fruit.isEmpty )
println( "Check if nums is empty : " + nums.isEmpty )
}
}

Concatenating Lists
You can use either ::: operator or List.:::() method or List.concat() method to add two or more lists.
Please find the following example given below −

Example
object ListExample2 {
def main(args: Array[String]) {
val fruit1 = "apples" :: ("oranges" :: ("pears" :: Nil))
val fruit2 = "mangoes" :: ("banana" :: Nil)

// use two or more lists with ::: operator


var fruit = fruit1 ::: fruit2
println( "fruit1 ::: fruit2 : " + fruit )

// use two lists with Set.:::() method


fruit = fruit1.:::(fruit2)
println( "fruit1.:::(fruit2) : " + fruit )
30

// pass two or more lists as arguments


fruit = List.concat(fruit1, fruit2)
println( "List.concat(fruit1, fruit2) : " + fruit )
}
}

Output
fruit1 ::: fruit2 : List(apples, oranges, pears, mangoes, banana)
fruit1.:::(fruit2) : List(mangoes, banana, apples, oranges, pears)
List.concat(fruit1, fruit2) : List(apples, oranges, pears, mangoes, banana)

Creating Uniform Lists


You can use List.fill() method to creates a list consisting of zero or more copies of the same element.
Try the following example program.

Example
object ListExample3 {
def main(args: Array[String]) {
val fruit =List.fill(3)("apples") // Repeats apples three times.
println( "fruit : " + fruit )

val num = List.fill(10)(2) // Repeats 2, 10 times.


println( "num : " + num )
}
}

Output
fruit : List(apples, apples, apples)
num : List(2, 2, 2, 2, 2, 2, 2, 2, 2, 2)

Tabulating a Function
You can use a function along with List.tabulate() method to apply on all the elements of the list before
tabulating the list.
Its arguments are just like those of List.fill: the first argument list gives the dimensions of the list to
create, and the second describes the elements of the list. The only difference is that instead of the
elements being fixed, they are computed from a function.

Example
object ListExample4 {
def main(args: Array[String]) {
31

// Creates 5 elements using the given function.


val squares = List.tabulate(6)(n => n * n)
println( "squares : " + squares )

val mul = List.tabulate( 4,5 )( _ * _ )


println( "mul : " + mul )
}
}

Output
squares : List(0, 1, 4, 9, 16, 25)
mul : List(List(0, 0, 0, 0, 0), List(0, 1, 2, 3, 4),
List(0, 2, 4, 6, 8), List(0, 3, 6, 9, 12))

Reverse List Order


You can use List.reverse method to reverse all elements of the list. The Following example shows the
usage.

Example
object Demo {
def main(args: Array[String]) {
val fruit = "apples" :: ("oranges" :: ("pears" :: Nil))

println( "Before reverse fruit : " + fruit )


println( "After reverse fruit : " + fruit.reverse )
}
}

Scala List Methods


Following are the important methods, which you can use while playing with Lists. For a complete list
of methods available, please check the official documentation of Scala.

No Methods with Description


1 def +(elem: A): List[A]: Prepends an element to this list
2 def ::(x: A): List[A]: Adds an element at the beginning of this list.
3 def :::(prefix: List[A]): List[A]: Adds the elements of a given list in front of this list.
4 def ::(x: A): List[A]: Adds an element x at the beginning of the list
5 def addString(b: StringBuilder): StringBuilder: Appends all elements of the list to a string builder.
def addString(b: StringBuilder, sep: String): StringBuilder: Appends all elements of the list to a string
6 builder using a separator string.
7 def apply(n: Int): A :Selects an element by its index in the list.
8 def contains(elem: Any): Boolean: Tests whether the list contains a given value as an element.
32

def copyToArray(xs: Array[A], start: Int, len: Int): Unit :Copies elements of the list to an array. Fills the
9 given array xs with at most length (len) elements of this list, beginning at position start.
10 def distinct: List[A]: Builds a new list from the list without any duplicate elements.
11 def drop(n: Int): List[A]: Returns all elements except first n ones.
12 def dropRight(n: Int): List[A]: Returns all elements except last n ones.
13 def dropWhile(p: (A) => Boolean): List[A]: Drops longest prefix of elements that satisfy a predicate.
14 def endsWith[B](that: Seq[B]): Boolean: Tests whether the list ends with the given sequence.
def equals(that: Any): Boolean: The equals method for arbitrary sequences. Compares this sequence to
15 some other object.
def exists(p: (A) => Boolean): Boolean: Tests whether a predicate holds for some of the elements of the
16 list.
17 def filter(p: (A) => Boolean): List[A]: Returns all elements of the list which satisfy a predicate.
18 def forall(p: (A) => Boolean): Boolean: Tests whether a predicate holds for all elements of the list.
19 def foreach(f: (A) => Unit): Unit: Applies a function f to all elements of the list.
20 def head: A: Selects the first element of the list.
def indexOf(elem: A, from: Int): Int: Finds index of first occurrence value in the list, after the index
21 position.
22 def init: List[A]: Returns all elements except the last.
def intersect(that: Seq[A]): List[A]: Computes the multiset intersection between the list and another
23 sequence.
24 def isEmpty: Boolean: Tests whether the list is empty.
25 def iterator: Iterator[A]: Creates a new iterator over all elements contained in the iterable object.
26 def last: A: Returns the last element.
def lastIndexOf(elem: A, end: Int): Int: Finds index of last occurrence of some value in the list; before or
27 at a given end index.
28 def length: Int :Returns the length of the list.
def map[B](f: (A) => B): List[B]: Builds a new collection by applying a function to all elements of this
29 list.
30 def max: A: Finds the largest element.
31 def min: A: Finds the smallest element.
32 def mkString: String: Displays all elements of the list in a string.
33 def mkString(sep: String): String: Displays all elements of the list in a string using a separator string.
34 def reverse: List[A]: Returns new list with elements in reversed order.
35 def sorted[B >: A]: List[A]: Sorts the list according to an Ordering.
def startsWith[B](that: Seq[B], offset: Int): Boolean
36 Tests whether the list contains the given sequence at a given index.
37 def sum: A: Sums up the elements of this collection.
38 def tail: List[A]: Returns all elements except the first.
39 def take(n: Int): List[A]: Returns first "n" elements.
40 def takeRight(n: Int): List[A]: Returns last "n" elements.
41 def toArray: Array[A]: Converts the list to an array.
33

42 def toBuffer[B >: A]: Buffer[B]: Converts the list to a mutable buffer.
43 def toMap[T, U]: Map[T, U]: Converts this list to a map.
44 def toSeq: Seq[A]: Converts the list to a sequence.
45 def toSet[B >: A]: Set[B]: Converts the list to a set.
46 def toString(): String: Converts the list to a string.

Scala - Sets
Scala Set is a collection of pairwise different elements of the same type. In other words, a Set is a
collection that contains no duplicate elements. There are two kinds of Sets, the immutable and the
mutable. The difference between mutable and immutable objects is that when an object is immutable,
the object itself can't be changed.
By default, Scala uses the immutable Set. If you want to use the mutable Set, you'll have to import
scala.collection.mutable.Set class explicitly. If you want to use both mutable and immutable sets in
the same collection, then you can continue to refer to the immutable Set as Set but you can refer to the
mutable Set as mutable.Set.
Here is how you can declare immutable Sets −

Syntax
// Empty set of integer type
var s : Set[Int] = Set()
// Set of integer type
var s : Set[Int] = Set(1,3,5,7)
or
var s = Set(1,3,5,7)

While defining an empty set, the type annotation is necessary as the system needs to assign a concrete
type to variable.

Basic Operations on set


All operations on sets can be expressed in terms of the following three methods −

No Methods & Description


1 Head: This method returns the first element of a set.
2 Tail: This method returns a set consisting of all elements except the first.
3 IsEmpty: This method returns true if the set is empty otherwise false.

Example1
object SetExample1 {
def main(args: Array[String]) {
34

val fruit = Set("apples", "oranges", "pears")


val nums: Set[Int] = Set()
println( "Head of fruit : " + fruit.head )
println( "Tail of fruit : " + fruit.tail )
println( "Check if fruit is empty : " + fruit.isEmpty )
println( "Check if nums is empty : " + nums.isEmpty )
}
}

Concatenating Sets
You can use either ++ operator or Set.++() method to concatenate two or more sets, but while adding
sets it will remove duplicate elements.
The Following is the example to concatenate two sets.

Example2
object SetExample2 {
def main(args: Array[String]) {
val fruit1 = Set("apples", "oranges", "pears")
val fruit2 = Set("mangoes", "banana")
// use two or more sets with ++ as operator
var fruit = fruit1 ++ fruit2
println( "fruit1 ++ fruit2 : " + fruit )
// use two sets with ++ as method
fruit = fruit1.++(fruit2)
println( "fruit1.++(fruit2) : " + fruit )
}
}

Find Max, Min Elements in a Set


You can use Set.min method to find out the minimum and Set.max method to find out the maximum of
the elements available in a set. Following is the example to show the program.

Example3
object SetExample3 {
def main(args: Array[String]) {
val num = Set(5,6,9,20,30,45)
// find min and max of the elements
println( "Min element in Set(5,6,9,20,30,45) : " + num.min )
println( "Max element in Set(5,6,9,20,30,45) : " + num.max )
}
}
35

Find Common Values Insets


You can use either Set.& method or Set.intersect method to find out the common values between two
sets.

Example4
object SetExample4 {
def main(args: Array[String]) {
val num1 = Set(5,6,9,20,30,45)
val num2 = Set(50,60,9,20,35,55)
// find common elements between two sets
println( "num1.&(num2) : " + num1.&(num2) )
println( "num1.intersect(num2) : " + num1.intersect(num2) )
}
}

Scala Set methods


Following are the important methods which you can use while playing with Sets. For a complete list of
methods available, please check official documentation of Scala.

No Methods with Description


1 def +(elem: A): Set[A]:Creates a new set with an additional element, unless the element is already present.
2 def -(elem: A): Set[A]:Creates a new set with a given element removed from this set.
3 def contains(elem: A): Boolean: Returns true if elem is contained in this set, false otherwise.
def &(that: Set[A]): Set[A]: Returns a new set consisting of all elements that are both in this set and in
4 the given set.
5 def &~(that: Set[A]): Set[A]: Returns the difference of this set and another set.
def +(elem1: A, elem2: A, elems: A*): Set[A]: Creates a new immutable set with additional elements
6 from the passed sets
def ++(elems: A): Set[A]: Concatenates this immutable set with the elements of another collection to this
7 immutable set.
def -(elem1: A, elem2: A, elems: A*): Set[A]: Returns a new immutable set that contains all elements of
8 the current immutable set except one less occurrence of each of the given argument elements.
def addString(b: StringBuilder): StringBuilder:Appends all elements of this immutable set to a string
9 builder.
def addString(b: StringBuilder, sep: String): StringBuilder:Appends all elements of this immutable set
10 to a string builder using a separator string.
11 def apply(elem: A):Tests if some element is contained in this set.
def count(p: (A) => Boolean): Int: Counts the number of elements in the immutable set which satisfy a
12 predicate.
13 def copyToArray(xs: Array[A], start: Int, len: Int): Unit: Copies elements of this immutable set to an
36

array.
14 def diff(that: Set[A]): Set[A]: Computes the difference of this set and another set.
15 def drop(n: Int): Set[A]]: Returns all elements except first n ones.
16 def dropRight(n: Int): Set[A]: Returns all elements except last n ones.
17 def dropWhile(p: (A) => Boolean): Set[A]: Drops longest prefix of elements that satisfy a predicate.
def equals(that: Any): Boolean: The equals method for arbitrary sequences. Compares this sequence to
18 some other object.
def exists(p: (A) => Boolean): Boolean: Tests whether a predicate holds for some of the elements of this
19 immutable set.
def filter(p: (A) => Boolean): Set[A]: Returns all elements of this immutable set which satisfy a
20 predicate.
def find(p: (A) => Boolean): Option[A]: Finds the first element of the immutable set satisfying a
21 predicate, if any.
def forall(p: (A) => Boolean): Boolean: Tests whether a predicate holds for all elements of this
22 immutable set.
23 def foreach(f: (A) => Unit): Unit: Applies a function f to all elements of this immutable set.
24 def head: A: Returns the first element of this immutable set.
25 def init: Set[A]: Returns all elements except the last.
26 def intersect(that: Set[A]): Set[A]: Computes the intersection between this set and another set.
27 def isEmpty: Boolean:Tests if this set is empty.
28 def iterator: Iterator[A]:Creates a new iterator over all elements contained in the iterable object.
29 def last: A:Returns the last element.
def map[B](f: (A) => B): immutable.Set[B]: Builds a new collection by applying a function to all
30 elements of this immutable set.
31 def max: A: Finds the largest element.
32 def min: A: Finds the smallest element.
33 def mkString: String: Displays all elements of this immutable set in a string.
def mkString(sep: String): String: Displays all elements of this immutable set in a string using a
34 separator string.
def product: A: Returns the product of all elements of this immutable set with respect to the * operator in
35 num.
36 def size: Int :Returns the number of elements in this immutable set.
def splitAt(n: Int): (Set[A], Set[A]): Returns a pair of immutable sets consisting of the first n elements of
37 this immutable set, and the other elements.
def subsetOf(that: Set[A]): Boolean: Returns true if this set is a subset of that, i.e. if every element of this
38 set is also an element of that.
39 def sum: A :Returns the sum of all elements of this immutable set with respect to the + operator in num.
def tail: Set[A]
40 Returns a immutable set consisting of all elements of this immutable set except the first one.
41 def take(n: Int): Set[A]: Returns first n elements.
42 def takeRight(n: Int):Set[A]: Returns last n elements.
43 def toArray: Array[A]: Returns an array containing all elements of this immutable set.
37

44 def toBuffer[B >: A]: Buffer[B]: Returns a buffer containing all elements of this immutable set.
45 def toList: List[A]: Returns a list containing all elements of this immutable set.
46 def toMap[T, U]: Map[T, U]: Converts this immutable set to a map
47 def toSeq: Seq[A]: Returns a seq containing all elements of this immutable set.
48 def toString(): String: Returns a String representation of the object.

Scala - Maps
Scala map is a collection of key/value pairs. Any value can be retrieved based on its key. Keys are
unique in the Map, but values need not be unique. Maps are also called Hash tables. There are two
kinds of Maps, the immutable and the mutable. The difference between mutable and immutable
objects is that when an object is immutable, the object itself can't be changed.
By default, Scala uses the immutable Map. If you want to use the mutable Map, you'll have to import
scala.collection.mutable.Map class explicitly. If you want to use both mutable and immutable Maps in
the same, then you can continue to refer to the immutable Map as Map but you can refer to the mutable
set as mutable.Map.
The Following is the example statements to declare immutable Maps −
// Empty hash table whose keys are strings and values are integers:
var A:Map[Char,Int] = Map()

// A map with keys and values.


val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")

While defining empty map, the type annotation is necessary as the system needs to assign a concrete
type to variable. If we want to add a key-value pair to a Map, we can use the operator + as follows.
A + = ('I' -> 1)
A + = ('J' -> 5)
A + = ('K' -> 10)
A + = ('L' -> 100)

Basic Operations on MAP


All operations on maps can be expressed in terms of the following three methods.

No Methods & Description


1 Keys This method returns an iterable containing each key in the map.
2 Values This method returns an iterable containing each value in the map.
3 IsEmpty : This method returns true if the map is empty otherwise false.
Try the following example program showing usage of the Map methods.
38

Example
object MapExample1 {
def main(args: Array[String]) {
val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF", "peru" -> "#CD853F")
val nums: Map[Int, Int] = Map()
println( "Keys in colors : " + colors.keys )
println( "Values in colors : " + colors.values )
println( "Check if colors is empty : " + colors.isEmpty )
println( "Check if nums is empty : " + nums.isEmpty )
}
}

Concatenating Maps
You can use either ++ operator or Map.++() method to concatenate two or more Maps, but while
adding Maps it will remove duplicate keys.
Try the following example program to concatenate two Maps.

Example
object MapExample2 {
def main(args: Array[String]) {
val colors1 = Map("red" -> "#FF0000", "azure" -> "#F0FFFF", "peru" -> "#CD853F")
val colors2 = Map("blue" -> "#0033FF", "yellow" -> "#FFFF00", "red" ->
"#FF0000")

// use two or more Maps with ++ as operator


var colors = colors1 ++ colors2
println( "colors1 ++ colors2 : " + colors )

// use two maps with ++ as method


colors = colors1.++(colors2)
println( "colors1.++(colors2)) : " + colors )
}
}

Output

colors1 ++ colors2 : Map(blue -> #0033FF, azure -> #F0FFFF,


peru -> #CD853F, yellow -> #FFFF00, red -> #FF0000)

colors1.++(colors2)) : Map(blue -> #0033FF, azure -> #F0FFFF,


peru -> #CD853F, yellow -> #FFFF00, red -> #FF0000)

Print Keys and Values from a Map


You can iterate through the keys and values of a Map using “foreach” loop. Here, we used method
foreach associated with iterator to walk through the keys. Following is the example program.
39

Example
object MapExample3 {
def main(args: Array[String]) {
val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF","peru" ->
"#CD853F")

colors.keys.foreach{ i =>
print( "Key = " + i )
println(" Value = " + colors(i) )}
}
}

Check for a key in Map


You can use either Map.contains method to test if a given key exists in the map or not. Try the
Following example program to key checking.

Example
object MapExample4 {
def main(args: Array[String]) {
val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF", "peru" -> "#CD853F")
if( colors.contains( "red" )) {
println("Red key exists with value :" + colors("red"))
} else {
println("Red key does not exist")
}
if( colors.contains( "maroon" )) {
println("Maroon key exists with value :" + colors("maroon"))
} else {
println("Maroon key does not exist")
}
}
}

Scala Map Methods


Following are the important methods which you can use while playing with Maps. For a complete list
of methods available, please check official documentation of Scala.

No Methods with Description


def ++(xs: Map[(A, B)]): Map[A, B] Returns a new map containing mappings of this map and those
1 provided by xs.
def -(elem1: A, elem2: A, elems: A*): Map[A, B] Returns a new map containing all the mappings of this
2 map except mappings with a key equal to elem1, elem2 or any of elems.
def --(xs: GTO[A]): Map[A, B] Returns a new map with all the key/value mappings of this map except
3 mappings with a key equal to a key from the traversable object xs.
4 def get(key: A): Option[B] :Optionally returns the value associated with a key.
40

5 def iterator: Iterator[(A, B)] :Creates a new iterator over all key/value pairs of this map
def addString(b: StringBuilder): StringBuilder
6 Appends all elements of this shrinkable collection to a string builder.
def addString(b: StringBuilder, sep: String): StringBuilder
7 Appends all elements of this shrinkable collection to a string builder using a separator string.
def apply(key: A): B
8 Returns the value associated with the given key, or the result of the map's default method, if none exists.
def clear(): Unit Removes all bindings from the map. After this operation has completed, the map will be
9 empty.
10 def clone(): Map[A, B] :Creates a copy of the receiver object.
11 def contains(key: A): Boolean: Returns true if there is a binding for key in this map, false otherwise.
def copyToArray(xs: Array[(A, B)]): Unit Copies values of this shrinkable collection to an array. Fills
12 the given array xs with values of this shrinkable collection.
def count(p: ((A, B)) => Boolean): Int
13 Counts the number of elements in the shrinkable collection which satisfy a predicate.
def default(key: A): B
14 Defines the default value computation for the map, returned when a key is not found.
15 def drop(n: Int): Map[A, B] : Returns all elements except first n ones.
16 def dropRight(n: Int): Map[A, B]: Returns all elements except last n ones
def dropWhile(p: ((A, B)) => Boolean): Map[A, B] Drops longest prefix of elements that satisfy a
17 predicate.
18 def empty: Map[A, B]: Returns the empty map of the same type.
def equals(that: Any): Boolean Returns true if both maps contain exactly the same keys/values, false
19 otherwise.
def exists(p: ((A, B)) => Boolean): Boolean Returns true if the given predicate p holds for some of the
20 elements of this shrinkable collection, otherwise false.
def filter(p: ((A, B))=> Boolean): Map[A, B]
21 Returns all elements of this shrinkable collection which satisfy a predicate.
def filterKeys(p: (A) => Boolean): Map[A, B] Returns an immutable map consisting only of those key
22 value pairs of this map where the key satisfies the predicate p.
def find(p: ((A, B)) => Boolean): Option[(A, B)]
23 Finds the first element of the shrinkable collection satisfying a predicate, if any.
def foreach(f: ((A, B)) => Unit): Unit
24 Applies a function f to all elements of this shrinkable collection.
25 def init: Map[A, B] Returns all elements except the last.
26 def isEmpty: Boolean Tests whether the map is empty.
27 def keys: Iterable[A] Returns an iterator over all keys.
28 def last: (A, B) Returns the last element.
29 def max: (A, B) Finds the largest element.
30 def min: (A, B) Finds the smallest element.
31 def mkString: String Displays all elements of this shrinkable collection in a string.
32 def product: (A, B) Returns the product of all elements of this shrinkable collection with respect to the *
operator in num.
41

def remove(key: A): Option[B] Removes a key from this map, returning the value associated previously
33 with that key as an option.
def retain(p: (A, B) => Boolean): Map.this.type Retains only those mappings for which the predicate p
34 returns true.
35 def size: Int Return the number of elements in this map.
def sum: (A, B) Returns the sum of all elements of this shrinkable collection with respect to the +
36 operator in num.
37 def tail: Map[A, B] Returns all elements except the first.
38 def take(n: Int): Map[A, B] Returns first n elements.
39 def takeRight(n: Int): Map[A, B] Returns last n elements.
def takeWhile(p: ((A, B)) => Boolean): Map[A, B]
40 Takes longest prefix of elements that satisfy a predicate.
41 def toArray: Array[(A, B)] Converts this shrinkable collection to an array.
42 def toBuffer[B >: A]: Buffer[B] Returns a buffer containing all elements of this map.
43 def toList: List[A] Returns a list containing all elements of this map.
44 def toSeq: Seq[A] Returns a seq containing all elements of this map.
45 def toSet: Set[A] Returns a set containing all elements of this map.
46 def toString(): String Returns a String representation of the object.

Scala - Tuples
Scala tuple combines a fixed number of items together so that they can be passed around as a whole.
Unlike an array or list, a tuple can hold objects with different types but they are also immutable.
The following is an example of a tuple holding an integer, a string, and the console.
val t = (1, "hello", Console)

Which is syntactic sugar (short cut) for the following −


val t = new Tuple3(1, "hello", Console)

The actual type of a tuple depends upon the number and of elements it contains and the types of those
elements.
Thus, the type of (99, "Luftballons") is Tuple2[Int, String].
The type of ('u', 'r', "the", 1, 4, "me") is Tuple6[Char, Char, String, Int, Int, String]
Tuples are of type Tuple1, Tuple2, Tuple3 and so on. Currently there is upper limit of 22 in the Scala.
if you need more, then you can use a collection, not a tuple. For each TupleN type, where 1 <= N <=
22, Scala defines a number of element-access methods. Given the following definition −
42

val t = (4,3,2,1)

To access elements of a tuple t, you can use method t._1 to access the first element, t._2 to access the
second, and so on. For example, the following expression computes the sum of all elements of t.
val sum = t._1 + t._2 + t._3 + t._4

You can use Tuple to write a method that takes a List[Double] and returns the count, the sum, and the
sum of squares returned in a three-element Tuple, a Tuple3[Int, Double, Double].
They are also useful to pass a list of data values as messages between actors in concurrent
programming.

Example1
object TupleExample1 {
def main(args: Array[String]) {
val t = (4,3,2,1)
val sum = t._1 + t._2 + t._3 + t._4
println( "Sum of elements: " + sum )
}
}

Iterate over the Tuple


You can use Tuple.productIterator() method to iterate over all the elements of a Tuple.

Example2
object TupleExample2 {
def main(args: Array[String]) {
val t = (4,3,2,1)
t.productIterator.foreach{ i =>println("Value = " + i )}
}
}

Converting to String
You can use Tuple.toString() method to concatenate all the elements of the tuple into a string.

Example3
object TupleExample3 {
def main(args: Array[String]) {
val t = new Tuple3(1, "hello", Console)
println("Concatenated String: " + t.toString() )
}
}
43

Swap the Elements


You can use Tuple.swap method to swap the elements of a Tuple2.

Example
object TupleExample4 {
def main(args: Array[String]) {
val t = new Tuple2("Scala", "hello")
println("Swapped Tuple: " + t.swap )
}
}

Scala - Options
Scala Option[ T ] is a container for zero or one element of a given type. An Option[T] can be either
Some[T] or None object, which represents a missing value. For instance, the get method of Scala's
Map produces Some(value) if a value corresponding to a given key has been found, or None if the
given key is not defined in the Map.
Option type is used frequently in Scala programs and you can compare this with the null value
available in Java which indicate no value.
For example, the get method of java.util.HashMap returns either a value stored in the HashMap, or null
if no value was found.
Let's say we have a method that retrieves a record from the database based on a primary key.
def findPerson(key: Int): Option[Person]

The method will return Some[Person] if the record is found but None if the record is not found. Let us
follow the following program.

Example1
object OptionExample1 {
def main(args: Array[String]) {
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")
println("capitals.get( \"France\" ) : " + capitals.get( "France" ))
println("capitals.get( \"India\" ) : " + capitals.get( "India" ))
}
}

The most common way to take optional values apart is through a pattern match. For example try the
following program.
44

Example2
object OptionExample2 {
def main(args: Array[String]) {
val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")
println("show(capitals.get( \"Japan\")) : " + show(capitals.get( "Japan")) )
println("show(capitals.get( \"India\")) : " + show(capitals.get( "India")) )
}

def show(x: Option[String]) = x match {


case Some(s) => s
case None => "?"
}
}

Using getOrElse() Method


Following is the example program to show how to use getOrElse() method to access a value or a
default when no value is present.

Example3
object OptionExample3 {
def main(args: Array[String]) {
val a:Option[Int] = Some(5)
val b:Option[Int] = None

println("a.getOrElse(0): " + a.getOrElse(0) )


println("b.getOrElse(10): " + b.getOrElse(10) )
}
}

Output
a.getOrElse(0): 5
b.getOrElse(10): 10

Using isEmpty() Method


Following is the example program to show how to use isEmpty() method to check if the option is None
or not.

Example4
object OptionExample4 {
def main(args: Array[String]) {
val a:Option[Int] = Some(5)
val b:Option[Int] = None

println("a.isEmpty: " + a.isEmpty )


println("b.isEmpty: " + b.isEmpty )
45

}
}

output
a.isEmpty: false
b.isEmpty: true

Scala Option Methods


Following are the important methods which you can use while playing with Options. For a complete
list of methods available, please check official documentation of Scala.

No Methods with Description


1 def get: A Returns the option's value.
2 def isEmpty: Boolean Returns true if the option is None, false otherwise.
3 def productArity: Int The size of this product. For a product A(x_1, ..., x_k), returns k
def productElement(n: Int): Any The nth element of this product, 0-based. In other words, for a
4
product A(x_1, ..., x_k), returns x_(n+1) where 0 < n < k.
def exists(p: (A) => Boolean): Boolean Returns true if this option is nonempty and the predicate
5
p returns true when applied to this Option's value. Otherwise, returns false.
def filter(p: (A) => Boolean): Option[A] Returns this Option if it is nonempty and applying the
6
predicate p to this Option's value returns true. Otherwise, return None.
def filterNot(p: (A) => Boolean): Option[A] Returns this Option if it is nonempty and applying
7
the predicate p to this Option's value returns false. Otherwise, return None.
def flatMap[B](f: (A) => Option[B]): Option[B] Returns the result of applying f to this
8
Option's value if this Option is nonempty. Returns None if this Option is empty.
def foreach[U](f: (A) => U): Unit Apply the given procedure f to the option's value, if it is
9
nonempty. Otherwise, do nothing.
def getOrElse[B >: A](default: => B): B Returns the option's value if the option is nonempty,
10
otherwise return the result of evaluating default.
11 def isDefined: Boolean Returns true if the option is an instance of Some, false otherwise.
def iterator: Iterator[A] Returns a singleton iterator returning the Option's value if it is
12
nonempty, or an empty iterator if the option is empty.
def map[B](f: (A) => B): Option[B] Returns a Some containing the result of applying f to this
13
Option's value if this Option is nonempty. Otherwise return None.
def orElse[B >: A](alternative: => Option[B]): Option[B]
14
Returns this Option if it is nonempty, otherwise return the result of evaluating alternative.
15 def orNull Returns the option's value if it is nonempty, or null if it is empty.

Scala - Iterators
An iterator is not a collection, but rather a way to access the elements of a collection one by one. The
46

two basic operations on an iterator it are next and hasNext. A call to it.next() will return the next
element of the iterator and advance the state of the iterator. You can find out whether there are more
elements to return using Iterator's it.hasNext method.
The most straightforward way to "step through" all the elements returned by an iterator is to use a while
loop. Let us follow the following example program.

Example1
object ItrtExample1 {
def main(args: Array[String]) {
val it = Iterator("a", "number", "of", "words")
while (it.hasNext){
println(it.next())
}
}
}

Find Min, Max Valued and Length of an Iterator


You can use it.min and it.max methods to find out the minimum and maximum valued elements from
an iterator. Here, we used ita and itb to perform two different operations because iterator can be
traversed only once.
You can use either it.size or it.length methods to find out the number of elements available in an
iterator.

Example2
object ItrtExample2{
def main(args: Array[String]) {
val ita = Iterator(20,40,2,50,69, 90)
val itb = Iterator(20,40,2,50,69, 90)
println("Maximum valued element " + ita.max )
println("Minimum valued element " + itb.min )
println("Value of ita.size : " + ita.size )
println("Value of itb.length : " + itb.length )
}
}

Scala Iterator Methods


Following are the important methods which you can use while playing with Iterator. For a complete list
of methods available, please check official documentation of Scala.

Sr.No Methods with Description


1 def hasNext: Boolean Tests whether this iterator can provide another element.
2 def next(): A Produces the next element of this iterator.
47

3 def ++(that: => Iterator[A]): Iterator[A] Concatenates this iterator with another.
def ++[B >: A](that :=> GenTraversableOnce[B]): Iterator[B]
4 Concatenates this iterator with another.
def addString(b: StringBuilder): StringBuilder Returns the string builder b to which elements were
5 appended.
def addString(b: StringBuilder, sep: String): StringBuilder
6 Returns the string builder b to which elements were appended using a separator string.
7 def buffered: BufferedIterator[A] Creates a buffered iterator from this iterator.
8 def contains(elem: Any): Boolean Tests whether this iterator contains a given value as an element.
def copyToArray(xs: Array[A], start: Int, len: Int): Unit
9 Copies selected values produced by this iterator to an array.
def count(p: (A) => Boolean): Int
10 Counts the number of elements in the traversable or iterator which satisfy a predicate.
def drop(n: Int): Iterator[A] Advances this iterator past the first n elements, or the length of the
11 iterator, whichever is smaller.
def dropWhile(p: (A) => Boolean): Iterator[A] Skips longest sequence of elements of this iterator
12 which satisfy given predicate p, and returns an iterator of the remaining elements.
def duplicate: (Iterator[A], Iterator[A]) Creates two new iterators that both iterate over the same
13 elements as this iterator (in the same order).
def exists(p: (A) => Boolean): Boolean Returns true if the given predicate p holds for some of the
14 values produced by this iterator, otherwise false.
def filter(p: (A) => Boolean): Iterator[A] Returns an iterator over all the elements of this iterator that
15 satisfy the predicate p. The order of the elements is preserved.
def filterNot(p: (A) => Boolean): Iterator[A]
16 Creates an iterator over all the elements of this iterator which do not satisfy a predicate p.
def find(p: (A) => Boolean): Option[A]
17 Finds the first value produced by the iterator satisfying a predicate, if any.
def flatMap[B](f: (A) => GenTraversableOnce[B]): Iterator[B] Creates a new iterator by applying a
18 function to all values produced by this iterator and concatenating the results.
def forall(p: (A) => Boolean): Boolean Returns true if the given predicate p holds for all values
19 produced by this iterator, otherwise false.
20 def foreach(f: (A) => Unit): Unit Applies a function f to all values produced by this iterator.
21 def hasDefiniteSize: Boolean Returns true for empty Iterators, false otherwise.
def indexOf(elem: B): Int Returns the index of the first occurrence of the specified object in this
22 iterable object.
def indexWhere(p: (A) => Boolean): Int Returns the index of the first produced value satisfying a
23 predicate, or -1.
24 def isEmpty: Boolean Returns true if hasNext is false, false otherwise.
25 def isTraversableAgain: Boolean Tests whether this Iterator can be repeatedly traversed.
def length: Int Returns the number of elements in this iterator. The iterator is at its end after this
26 method returns.
27 def map[B](f: (A) => B): Iterator[B] Returns a new iterator which transforms every value produced
by this iterator by applying the function f to it.
28 def max: A Finds the largest element. The iterator is at its end after this method returns.
48

29 def min: A Finds the minimum element. The iterator is at its end after this method returns.
30 def mkString: String Displays all elements of this traversable or iterator in a string.
def mkString(sep: String): String
31 Displays all elements of this traversable or iterator in a string using a separator string.
32 def nonEmpty: Boolean Tests whether the traversable or iterator is not empty.
def padTo(len: Int, elem: A): Iterator[A]
33 Appends an element value to this iterator until a given target length is reached.
def patch(from: Int, patchElems: Iterator[B], replaced: Int): Iterator[B]
34 Returns this iterator with patched values.
35 def product: A Multiplies up the elements of this collection.
def sameElements(that: Iterator[_]): Boolean
36 Returns true, if both iterators produce the same elements in the same order, false otherwise.
37 def seq: Iterator[A] Returns a sequential view of the collection.
38 def size: Int Returns the number of elements in this traversable or iterator.
def slice(from: Int, until: Int): Iterator[A]
39 Creates an iterator returning an interval of the values produced by this iterator.
def sum: A Returns the sum of all elements of this traversable or iterator with respect to the + operator
40 in num.
def take(n: Int): Iterator[A] Returns an iterator producing only of the first n values of this iterator, or
41 else the whole iterator, if it produces fewer than n values.
42 def toArray: Array[A] Returns an array containing all elements of this traversable or iterator.
43 def toBuffer: Buffer[B] Returns a buffer containing all elements of this traversable or iterator.
def toIterable: Iterable[A] Returns an Iterable containing all elements of this traversable or iterator.
44 This will not terminate for infinite iterators.
def toIterator: Iterator[A] Returns an Iterator containing all elements of this traversable or iterator.
45 This will not terminate for infinite iterators.
46 def toList: List[A] Returns a list containing all elements of this traversable or iterator.
47 def toMap[T, U]: Map[T, U] Returns a map containing all elements of this traversable or iterator.
48 def toSeq: Seq[A] Returns a sequence containing all elements of this traversable or iterator.
49 def toString(): String Converts this iterator to a string.
def zip[B](that: Iterator[B]): Iterator[(A, B) Returns a new iterator containing pairs consisting of
50 corresponding elements of this iterator. The number of elements returned by the new iterator is same as
the minimum number of elements returned by the iterator (A or B).

Scala - Extractors
• An extractor in Scala is an object that has a method called unapply as one of its members. The
purpose of that unapply method is to match a value and take it apart.
49

• Often, the extractor object also defines a dual method apply for building values, but this is not
required.

Example
Let us take an example of object that defines both apply and unapply methods.
The apply method has the same meaning as always: it turns Test into an object that can be applied to
arguments in parentheses in the same way a method is applied. So you can write Test ("Zara",
"gmail.com") to construct the string "[email protected]".
The unapply method is what turns Test class into an extractor and it reverses the construction process
of apply.
Where apply takes two strings and forms an email address string out of them, unapply takes an email
address and returns potentially two strings: the user and the domain of the address.
The unapply must also handle the case where the given string is not an email address. That's why
unapply returns an Option-type over pairs of strings. Its result is either Some (user, domain) if the
string str is an email address with the given user and domain parts, or None, if str is not an email
address.

Understanding the apply() and Factory Methods


Let us consider a User class with default constructor:
class User(val name: String, val age: Int)

To create an instance of User, we use the new keyword to invoke the constructor:
val user = new User("James", 50)

We can use the apply() method as a factory method in the User’s companion object:
object User {
def apply(name: String, age: Int) = new User(name, age)
}

Now, we can call the apply() method, to instantiate our User class:
val user = User.apply("James", 50)

This can even be shortened by calling the class name without the apply() method:
val user = User("James", 50)

Also, we can write multiple apply() methods for our User class:
object User {
50

def apply(name: String, age: Int) = new User(name, age)


def apply(name: String) = new User(name, 0)
}

By calling User(“Odera”), we can instantiate User with an age of zero.

unapply() and Extractor Objects


The unapply() method is just the opposite of the apply() method. This method extracts all object
values and lists them as a result.
The best practice is to put the unapply() method in the companion object of our class:
object User {
def unapply(u: User): Option[(String, Int)] = Some(u.name, u.age)
}

Now we can call unapply() explicitly:


scala> User.unapply(user)
res8: Option[(String, Int)] = Some((jack,30))

This is how pattern matching in Scala extracts object values. Without the unapply() method, we can’t
do pattern matching:
user match {
case User(_, age) if age < 18 =>
println("You are not allowed to vote.")
case User(_, age) if age >= 18 =>
println("You can vote.")
}

Keep in mind that we can write a customized unapply() method to extract more details from our
objects. Think of an extractor object that extracts day, month, and year from a date-time string like
“2019-05-28”.

The unapplySeq() and Deconstructing Sequences


The unapply() method is useful when we’re going to extract various single-value items, such as the
name and age from a User.
On the other hand, if we want to deconstruct an instance to a collection of values, we should use
the unapplySeq() method.

For example, let’s suppose we’re going to deconstruct an HttpRequest instance to its Headers:
case class Header(key: String, value: String)
class HttpRequest(val method: String, val uri: String, val headers: List[Header])
51

If we declare a unapplySeq() method in the companion object:


object HttpRequest {
def unapplySeq(request: HttpRequest): Option[List[Header]] =
Some(request.headers)
}

Then we can deconstruct the request into its constituent headers and apply pattern matching to that
collection of headers:
val headers = Header("Content-Type", "application/json") ::
Header("Authorization", "token") :: Header("Content-Language", "fa_IR") :: Nil
val request = new HttpRequest("GET", "localhost", headers)
request match {
case HttpRequest(h1) => println(s"The only header is $h1")
case HttpRequest(h1, h2) => println(s"We have two headers: $h1 and $h2")
case HttpRequest(all @ _*) => print(s"All headers are as following: $all")
}

The first and second cases will be matched if the request contains one or two headers, respectively. In
the latter case, we bind a repeated number of headers (“_*” part) to the variable all. Obviously, the
latter case will match the given request, so this program prints:
All headers are as following: List(Header(Content-Type,application/json),
Header(Authorization,token), Header(Content-Language,fa_IR))

If we replace the unapplySeq() method with unapply(), the same pattern will fail with the message:
too many patterns for object HttpRequest offering List[Header]: expected 1, found 2

Put simply, when using unapply(), the whole List[Header] will be considered as a single value,
whereas unapplySeq() allows us to pattern match against each member of the sequence.

Syntax
unapply("[email protected]") equals Some("Zara", "gmail.com")
unapply("Zara Ali") equals None

Example: Extractor object for email addresses


object ExtractExample1 {
def main(args: Array[String]) {
println ("Apply method : " + apply("Zara", "gmail.com"));
println ("Unapply method : " + unapply("[email protected]"));
println ("Unapply method : " + unapply("Zara Ali"));
}
52

// The injection method (optional)


def apply(user: String, domain: String) = {
user +"@"+ domain
}

// The extraction method (mandatory)


def unapply(str: String): Option[(String, String)] = {
val parts = str split "@"

if (parts.length == 2){
Some(parts(0), parts(1))
} else {
None
}
}
}

Output
Apply method : [email protected]
Unapply method : Some((Zara,gmail.com))
Unapply method : None

Pattern Matching with Extractors


When an instance of a class is followed by parentheses with a list of zero or more parameters, the
compiler invokes the apply method on that instance. We can define apply both in objects and in
classes.
As mentioned above, the purpose of the unapply method is to extract a specific value we are looking
for. It does the opposite operation apply does. When comparing an extractor object using the match
statement the unapply method will be automatically executed.

Example
object ExtractExample2 {
def main(args: Array[String]) {
val x = Demo(5)
println(x)

x match {
case Demo(num) => println(x+" is bigger two times than "+num)

//unapply is invoked
case _ => println("i cannot calculate")
}
}
def apply(x: Int) = x*2
def unapply(z: Int): Option[Int] = if (z%2==0) Some(z/2) else None
}

You might also like