Scala Option if exists then set multiple vals if None set same multiple vals to empty string - scala

I have a Option employee object. From employee I want to get the name, department, address, number, age or anything else from it if it exists but if None the name, department, and everything else I want to set to "".
I would like to just do like in Java:
if (employee.isDefined) {
val name = employee.get.getEmployeName
val department = employee.get.getDepartment
val address = employee.get.getAddress
val number = employee.get.getNumber
val age = employee.get.getAge
} else {
val name, department, address, number, age = ""
}
but I learned it does not work like that. It looks like I would need another employee object and set the values like and then access it later:
if (employee.isDefined) {
emp.setName(employee.get.getEmployeName)
emp.setDepartment(employee.get.getDepartment)
...
} else {
emp.setName("")
emp.setDepartment("")
...
}
I also experimented with tuples?
val employeeInfo = employee match {
case Some(emp) => (employee.getEmployeName, employee.getDepartment, employee.getAddress,
employee.getNumber, employee.getAge)
case None => ("", "", "", "", "")
}
val name = employeeInfo._1
val department = employeeInfo._2
val address = employeeInfo._3
...
Are these methods okay? Or are there any better ways to do this? Thanks for the help

.getOrElse() is the usual means of extracting a value from an Option while specifying a default if the option is None.
In your case, however, it is the container of many values that might be None. For that I'd recommend .fold().
case class Employee(empName : String
,dept : String
,addr : String
,num : String
,age : String)
val employee: Option[Employee] =
Some(Employee("Jo","mkt","21A","55","44"))
//or None
val name = employee.fold("")(_.empName)
val department = employee.fold("")(_.dept)
val address = employee.fold("")(_.addr)
val number = employee.fold("")(_.num)
val age = employee.fold("")(_.age)
But I have to agree with the comments from #sinanspd, your overall design is questionable at best.

This is how I would tackle this specific operation:
val (name, department, address, number, age) =
employee.fold(("", "", "", "", "")) { e =>
(e.getEmployeName, e.getDepartment, e.getAddress, e.getNumber, e.getAge)
}
But as suggested in the comments, it is worth looking at the overall design. For example it may be better to keep the values optional:
val employeeData: Option[(String, String, String, String, String)] =
employee.map{ e =>
(e.getEmployeName, e.getDepartment, e.getAddress, e.getNumber, e.getAge)
}
This allows you to tell whether a value is "" because employee was None or because the value in the Employee object was "". And you would probably define a different class to represent this restricted set of employee data to make the code cleaner and clearer.

Related

Find a property value in the list of objects in scala

class Student {
var name: String = _
var stId: String = _
var courseName: String = _
}
object Student {
def apply(name: String, stId: String, courseName: String): Student = {
var s = new Student
s.name = name
s.stId = stId
s.courseName = courseName
s
}
}
val studentList :MutableList[Student]= MutableList()
studentList+=(Student("Alex","TI178","Math"))
studentList+=(Student("Bob","TI654","Comp"))
studentList+=(Student("Sam","TI1115","Comp"))
studentList+=(Student("Don","TI900","Math"))
how to find the list of student.stId who enrolled in "Math" or given value in the above MutableList?
studentList.filter(_.courseName=="Math").map(_.stId)
It's hard to tell without knowing what exactly a MutableList is. But assuming it's a scala.collection.mutable.MutableList you can do something like this:
studentList.collect {
case s if s.courseName == "Math" => s.stId
}
To find the students who enrolled in a given course you can use the filter function, which creates a new collection with only the elements for which the given function returns true:
studentList.filter(_.courseName == "Math")
then, to get the ids, you can use map, which returns a new collection by applying the given function to each element and collecting the results:
studentList.filter(_.courseName == "Math").map(_.stId)
With collect, you just traverse the collection just once and you can achieve the desire result that you want!
studentList.collect {
case student if student.courseName == "Math" => student.stId
}
So effectively collect is filter and map in just one step!

How to print a Monocle Lens as a property accessor style string

Using Monocle I can define a Lens to read a case class member without issue,
val md5Lens = GenLens[Message](_.md5)
This can used to compare the value of md5 between two objects and fail with an error message that includes the field name when the values differ.
Is there a way to produce a user-friendly string from the Lens alone that identifies the field being read by the lens? I want to avoid providing the field name explicitly
val md5LensAndName = (GenLens[Message](_.md5), "md5")
If there is a solution that also works with lenses with more than one component then even better. For me it would be good even if the solution only worked to a depth of one.
This is fundamentally impossible. Conceptually, lens is nothing more than a pair of functions: one to get a value from object and one to obtain new object using a given value. That functions can be implemented by the means of accessing the source object's fields or not. In fact, even GenLens macro can use a chain field accessors like _.field1.field2 to generate composite lenses to the fields of nested objects. That can be confusing at first, but this feature have its uses. For example, you can decouple the format of data storage and representation:
import monocle._
case class Person private(value: String) {
import Person._
private def replace(
array: Array[String], index: Int, item: String
): Array[String] = {
val copy = Array.ofDim[String](array.length)
array.copyToArray(copy)
copy(index) = item
copy
}
def replaceItem(index: Int, item: String): Person = {
val array = value.split(delimiter)
val newArray = replace(array, index, item)
val newValue = newArray.mkString(delimiter)
Person(newValue)
}
def getItem(index: Int): String = {
val array = value.split(delimiter)
array(index)
}
}
object Person {
private val delimiter: String = ";"
val nameIndex: Int = 0
val cityIndex: Int = 1
def apply(name: String, address: String): Person =
Person(Array(name, address).mkString(delimiter))
}
val name: Lens[Person, String] =
Lens[Person, String](
_.getItem(Person.nameIndex)
)(
name => person => person.replaceItem(Person.nameIndex, name)
)
val city: Lens[Person, String] =
Lens[Person, String](
_.getItem(Person.cityIndex)
)(
city => person => person.replaceItem(Person.cityIndex, city)
)
val person = Person("John", "London")
val personAfterMove = city.set("New York")(person)
println(name.get(personAfterMove)) // John
println(city.get(personAfterMove)) // New York
While not very performant, that example illustrates the idea: Person class don't have city or address fields, but by wrapping data extractor and a string rebuild function into Lens, we can pretend it have them. For more complex objects, lens composition works as usual: inner lens just operates on extracted object, relying on outer one to pack it back.

How can i convert this nonfunctional scala code with immutable members to an elegant solution?

How to avoid mutable index and make this more elegant?I know Null has to be changed with Option , i am just curious about the answers.
class Person(val name: String, val department: String)
var people = Array(new Person(“Jones”, “Marketing”), new Person(“Smith”, “Engineering”))
var engineer: Person = null
var index = 0
while (index < people.length) {
if (people(index).department == “Engineering”)
engineer = people(index)
index = index + 1
}
println(engineer.name + “ is an engineer”)
class Person(val name: String, val department: String)
val people = Array(new Person(“Jones”, “Marketing”), new Person(“Smith”, “Engineering”))
// Option[ Person ]... None if no Person satisfy this condition... Some( p ), if Person p is the first Person to satisfy the condition.
val personOption = people.find( p => p.department == "Engineering" )
personOption match {
case Some( p ) => println( " Found one engineer - " + p )
case None => println( "No engineer" )
}
If you want to find the last engineer in the array, you would probably use:
case class Person(val name: String, val department: String)
val people = Array(Person(“Jones”, “Marketing”), Person(“Smith”, “Engineering”))
def findLastEngineer(l: Seq[Person]) : Option[Person] =
people.foldLeft(None) {
case (previousOpt, eng) => if (eng.department == "Engineering") Some(eng) else previousOpt
}
}
println(findLastEngineer(people).map(_.name).getOrElse("Not found"))
I would do something like this:
case class Person(name: String, department: String)
val people = List(Person("Jones", "Marketing"), Person("Smith", "Engineering"))
val engineers = people.filter { person:Person => person.department == "Engineering" }
engineers.map { engineer: Person => println(engineer.name + " is an engineer") }
Try to use functions to transform your types in others. Usually we use map/reduce/filter functions to do this.
Here's how I would refactor:
// Refactored into a case class, since it's a simple data container
case class Person(name: String, department: String)
// Using the case class convenience apply method to drop `new`
val people = Array(Person(“Jones”, “Marketing”), Person(“Smith”, “Engineering”))
// Selects all the engineers. You could add `.headOption` to get the first.
val engineers = people.filter(_.department == "Engineering")
// Functional way of iterating the collection of engineers
// Also, using string interpolation to print
for (engineer <- engineers) println(s"${engineer.name} is an engineer.")
Alternatively, you could use collect to filter and pick the name:
// Collect is kind of a like a handy filter + map
val engineerNames = people.collect {
case Person(name, "Engineering") => name
}
for (name <- engineerNames) println(s"$name is an engineer.")
One last tip, if your departments are some finite set of fixed options, you should probably also consider making it a type:
sealed trait Department
case object Engineering extends Department
case object Marketing extends Department
// ... for each valid department
And then you can match on identity, rather than value. This lets you rely on the type system instead of constantly having to validate strings (known to some as stringly-typed programming). Best practice is to validate your data as early as possible into types, deal with it as typed data, and then only convert back to string for exporting data back out of your system (e.g. printing to screen, logging, serving via API).
You can use find to find first:
people
.find { _.department == "Engineering" }
.foreach { engineer => println(engineer.name + " is an engineer") }
or filter to find all:
people
.filter { _.department == "Engineering" }
.foreach { engineer => println(engineer.name + " is an engineer") }
By the way you can fix your code just by moving increment operation outside the if block:
if (people(index).department == "Engineering") {
engineer = people(index)
// index = index + 1
}
index = index + 1
After that you should check engineer for null, because your array may not contain a Person for your condition.
So it looks like you want to find last Person, thus you can use:
people
.foldLeft(None: Option[Person])((r, p) =>
if (p.department == "Engineering") Some(p) else r)
.foreach { engineer => println(engineer.name + " is an engineer") }
Also after avoiding all vars you can also change your Array (which is mutable structure) to List (by default scala.collection.immutable.List)

Scala local variable inside primary constructor

How in Scala I can define local variable in primary constructor?
I need to solve this exercise from Scala for the impatient book:
Write a class Person with a primary constructor that accepts a string
containing a first name, a space, and a last name, such as new
Person("Fred Smith"). Supply read-only properties firstName and
lastName. Should the primary constructor parameter be a var, a val, or
a plain parameter? Why?
And for now my solution looks like this:
class Person(firstLast: String) {
private[this] val firstLastAsArr = firstLast.trim.split(" ")
val firstName = firstLastAsArr (0)
val lastName = firstLastAsArr (1)
}
How I can restrict firstLastAsArr variable visibility to primary constructor scope (now it have class scope)?
One solution is to initialize firstName and lastName at once, thereby allowing to turn firstLastAsArr into a local temporary value inside your initialization block:
class Person(firstLast: String) {
val (firstName, lastName) = {
val firstLastAsArr = firstLast.trim.split(" ")
(firstLastAsArr(0), firstLastAsArr(1))
}
}
It is not a general answer, but in this particular way you may write:
val Array(firstName, lastName) = firstLast.trim.split(" ")
You don't strictly need the intermediate variable:
class Person(firstLast: String) {
val (firstName, lastName) =
firstLast.trim.split(" ") match {case Array(first, last) => (first, last)}
}
However, if your transformation from firstLast to firstName and lastName grows a big longer, for example, because you check that there is exactly one first and one last name, then I would encapsulate the whole splitting-business in a dedicated method:
class Person(firstLast: String) {
val (firstName, lastName) = split(firstLast)
private def split(firstLast: String): (String, String) = {
val firstLastAsArr = firstLast.trim.split(" ")
...
(first, last)
}
}
Pattern matching in constructor works just fine, but you should consider moving such logic from constructor to factory method:
case class Person(firstName: String, lastName: String)
object Person{
def apply(firstLast: String) = {
val firstLastAsArr = firstLast.trim.split(" ")
new Person(firstLastAsArr(0), firstLastAsArr(1))
}
}
val p = Person("My Title")
Pattern maching in primary constructor works well
class Person(_fullName:String) {
val (firstName, lastName) = _fullName.split(" ") match {
case Array(x:String, y:String, _*) => (x,y)
case _ => (null,null)
}
}
See my github for full answer
https://github.com/BasileDuPlessis/scala-for-the-impatient/blob/master/src/main/scala/com/basile/scala/ch05/Ex07.scala

Case Classes with optional fields in Scala

For example, I have this case class:
case class Student (firstName : String, lastName : String)
If I use this case class, is it possible that supplying data to the fields inside the case class are optional? For example, I'll do this:
val student = new Student(firstName = "Foo")
Thanks!
If you just want to miss the second parameter without a default information, I suggest you to use an Option.
case class Student(firstName: String, lastName: Option[String] = None)
Now you might create instances this way:
Student("Foo")
Student("Foo", None) // equal to the one above
Student("Foo", Some("Bar")) // neccesary to add a lastName
To make it usable as you wanted it, I will add an implicit:
object Student {
implicit def string2Option(s: String) = Some(s)
}
Now you are able to call it those ways:
import Student._
Student("Foo")
Student("Foo", None)
Student("Foo", Some("Bar"))
Student("Foo", "Bar")
You were close:
case class Student (firstName : String = "John", lastName : String = "Doe")
val student = Student(firstName = "Foo")
Another possibility is partially applied function:
case class Student (firstName : String, lastName : String)
val someJohn = Student("John", _: String)
//someJohn: String => Student = <function1>
val johnDoe = someJohn("Doe")
//johnDoe: Student = Student(John,Doe)
And to be complete, you can create some default object and then change some field:
val johnDeere = johnDoe.copy(lastName="Deere")
//johnDeer: Student = Student(John,Deere)
I would see two ways this is normally done.
1. default parameters
case class Student (firstName : String, lastName : String = "")
Student("jeypijeypi") # Student(jeypijeypi,)
2. alternative constructors
case class Student (firstName : String, lastName : String)
object Student {
def apply(firstName: String) = new Student(firstName,"")
}
Student("jeypijeypi") # Student(jeypijeypi,)
Which one is better depends slightly on the circumstances. The latter gives you more freedom: you can make any parameter(s) optional, or even change their order (not recommended). Default parameters need always to be at the end of the parameter list, I think. You can also combine these two ways.
Note: within the alternative constructors you need new to point the compiler to the actual constructor. Normally new is not used with case classes.