How do I declaratively create a list in Scala? - scala

In C# I can declare a list declaratively, in other words declare its structure and initialise it at the same time as follows:
var users = new List<User>
{
new User {Name = "tom", Age = 12},
new User {Name = "bill", Age = 23}
};
Ignoring the differences between a List in .Net and a List in Scala (ie, feel free to use a different collection type), is it possible to do something similar in Scala 2.8?
UPDATE
Adapting Thomas' code from below I believe this is the nearest equivalent to the C# code shown:
class User(var name: String = "", var age: Int = 0)
val users = List(
new User(name = "tom", age = 12),
new User(name = "bill", age = 23))

What about:
case class User(name: String, age: Int)
val users = List(User("tom", 12), User("bill", 23))
which will give you:
users: List[User] = List(User(tom,12), User(bill,23))

val users = User("tom", 12) :: User("bill", 23) :: Nil
You could also use Scalas tupel class:
val users = ("tom", 12) :: ("bill", 23) :: Nil

Or you can create objects without use of explicit class defined in your compilation module this wayList(
new {var name = "john"; var age = 18},
new {var name = "mary"; var age = 21}
)
Note, that this code has some serious drawback, it will create an anonymous class per each new.

Adapting Thomas' code from below I believe this is the nearest equivalent to the C# code shown:
class User(var name: String = "", var age: Int = 0)
val users = List(
new User(name = "tom", age = 12),
new User(name = "bill", age = 23))
It is subtly different to the way the C# code behaves because we are providing an explicit constructor with default values rather than using the no args constructor and setting properties subsequently, but the end result is comparable.

Related

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

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.

replace list element with another and return the new list

I am kind of stuck with this, and I know this is a bloody simple question :(
I have a case class such as:
case class Students(firstName: String, lastName: String, hobby: String)
I need to return a new list but change the value of hobby based on Student name. For example:
val classToday = List(Students("John","Smith","Nothing"))
Say if student name is John I want to change the hobby to Soccer so the resulting list should be:
List(Students("John","Smith","Soccer")
I think this can be done via map? I have tried:
classToday.map(x => if (x.firstName == "John") "Soccer" else x)
This will just replace firstName with Soccer which I do not want, I tried setting the "True" condition to x.hobby == "Soccer" but that does not work.
I think there is a simple solution to this :(
The lambda function in map has to return a Students value again, not just "Soccer". For example, if you had to replace everyone's hobbies with "Soccer", this is not right:
classToday.map(x => "Soccer")
What you want is the copy function:
classToday.map(x => x.copy(hobby = "Soccer"))
Or for the original task:
classToday.map(x => if (x.firstName == "John") x.copy(hobby = "Soccer") else x)
You can use pattern-matching syntax to pretty up this type of transition.
val newList = classToday.map{
case s#Students("John",_,_) => s.copy(hobby = "Soccer")
case s => s
}
I suggest to make it more generic, you can create a map of names to hobbies:
For example:
val firstNameToHobby = Map("John" -> "Soccer", "Brad" -> "Basketball")
And use it as follows:
case class Students(firstName: String, lastName: String, hobby: String)
val classToday = List(Students("John","Smith","Nothing"), Students("Brad","Smith","Nothing"))
val result = classToday.map(student => student.copy(hobby = firstNameToHobby.getOrElse(student.firstName, "Nothing")))
// result = List(Students(John,Smith,Soccer), Students(Brad,Smith,Basketball))
It would be better if you can create a mapping between the firstName of the student with Hobby, then you can use it like this:
scala> val hobbies = Map("John" -> "Soccer", "Messi" -> "Soccer", "Williams" -> "Cricket")
hobbies: scala.collection.immutable.Map[String,String] = Map(John -> Soccer, Messi -> Soccer, Williams -> Cricket)
scala> case class Student(firstName: String, lastName: String, hobby: String)
defined class Student
scala> val students = List(Student("John", "Smith", "Nothing"), Student("Williams", "Lopez", "Nothing"), Student("Thomas", "Anderson", "Nothing"))
students: List[Student] = List(Student(John,Smith,Nothing), Student(Williams,Lopez,Nothing), Student(Thomas,Anderson,Nothing))
scala> students.map(student => student.copy(hobby = hobbies.getOrElse(student.firstName, "Nothing")))
res2: List[Student] = List(Student(John,Smith,Soccer), Student(Williams,Lopez,Cricket), Student(Thomas,Anderson,Nothing))

Scala: How to get Enumeration element based on values and labels?

I have two enum classes defined as the following.
object Rating extends Enumeration {
type Rating = Value
val LOW: Value = Value("Low")
val AVERAGE: Value = Value("Average")
val HIGH: Value = Value("High")
}
object Reason extends Enumeration {
type Reason = Value
val MISSING_PARTS: Value = Value(1)
val WRONG_ITEM: Value = Value(2)
val DEFECTIVE_ITEM: Value = Value(3)
}
How can I get the Rating based on the String values "Low", "Average" and "High"
How can I get the Reason based on the Integer values 1, 2, 3
How can I get the Reason based on the val name MISSING_PARTS, WRONG_ITEM, DEFECTIVE_ITEM
Please correct me if I am not using the correct terminology. I am from Java background and new to Scala. I did a lot of searches but either they are outdated or they use very trivial examples where the labels and values are the same strings, which does not help much.
Any help will be appreciated.
Rating.withName("Low")
Reason(1) (which is shorthand for Reason.apply(1))
Reason.withName("DEFECTIVE_ITEM")
Explanation:
Each enumeration value has an id and a name. Both are generated using a default unless you override them using the appropriate Value(...) overload. In this case, Rating has customized names and Reason has customized IDs. Given the code above, these are the names and ids assigned to each value:
val LOW: Value = Value("Low") // Id: 0; Name: Low
val AVERAGE: Value = Value("Average") // Id: 1; Name: Average
val HIGH: Value = Value("High") // Id: 2; Name: High
val MISSING_PARTS: Value = Value(1) // Id: 1; Name: MISSING_PARTS
val WRONG_ITEM: Value = Value(2) // Id: 2; Name: WRONG_ITEM
val DEFECTIVE_ITEM: Value = Value(3) // Id: 3; Name: DEFECTIVE_ITEM
Now, we can access specific values using either withName(name: String) or apply(id: Int) which references values based on names and ids respectively.

In Scala how do you update a property of an object within an array of objects, the object being selected by the user

In Scala how do you update a property of an object within an array of objects, the object being selected by the user?
The object is being previously selected by the user via its ID number (which is the first field of the object). I would like to update the last field of the object.
My code:
case class Order(oId :Int, dt :String, cId :Int, sta :String) {
var orderId = oId
var dateTime = dt
var customerId = cId
var status = sta
}
def main(args: Array[String]) {
//New Orders
var o1 = new Order(1, "13 JUN 2016 12:30", 1, "New")
var o2 = new Order(2, "13 JUN 2016 12:32", 2, "New")
var o3 = new Order(3, "14 JUN 2016 12:30", 3, "New")
var o4 = new Order(4, "14 JUN 2016 12:32", 4, "New")
var orders1 = Array(o1,o2,o3,o4)
//print list of orders
val printList = readLine("Type yes to print a list of orders?")
if ((printList == "yes") || (printList == "Yes")){
orders1.foreach { println }
}
val orderNo = readLine("Which order would you like to view?").toInt
for(i <- 0 to orders1.length - 1) {
if(orders1(i).orderId == orderNo) {
println(xxxx)
val newStatus = readLine("Type picked to update status")
if (newStatus == "picked"){
println (newStatus)
orders1(orderNo).status = newStatus
orders1.foreach { println }
}
} else {
}
}
}
Scala's Array wouldn't allow you to add or remove elements by but it allows changing the referenced elements at a given position:
val a = Array[Int](1,2,3)
a(1) = 0
However if you want to change the property of an object, that property should be mutable. For example, if the objects contained by your array are instances of ordinary case classes as:
case class C(x: Int, y: String)
val a = Array(C(1,"hello"), C(2,"bye"))
Then you won't be able to change, lets say, property y without creating a new element sharing the same value for the other properties:
a(1) = a(1).copy(y = "good bye")
In the line above, you'll be creating a new instance of C and assigning it to the position 1.
On the other hand, if your elements are instances of classes with mutable properties such as:
val b = Array(new MC(1,"hello"), new MC(2,"bye"))
You won't have any problem to change their properties:
b(1).y = "good bye"
EDIT: Based on your code:
Order defined as:
case class Order(ordId_ :Int, datetime_ :String, custId_ :Int, status_ :String) {
var orderId = ordId_
var dateTime = datetime_
var customerId = custId_
var status = status_
}
Have 8 attributes. That is duplicated attributes. If you want to make you case class attributes mutable you should mark them as var in the case class attributes list:
case class Order(var ordId :Int, var datetime :String, var custId :Int, var status :String)
Instead of duplicating them.

how to store object state before and after setting attribute values

i have a class Demo i want to save object value before setting attribute value here is my code
case class Demo (var abc:String)
val d =Demo("bob")
var oldDemo=d
d.abc="joe"
println("old name is "+oldDemo.abc +" the new name is "+d.abc)
the output printed on the console is
old name is joe the new name is joe
i want to store object value before setting d.abc="joe" so that i can get bob when i do oldDemo.abc
please guide me where i am going wrong and what is the right way to achieve my goal .and i apologize if i am doing something stupid
You can use copy() on a case class.
val d1 = Demo("abc")
val d2 = d1.copy()
d1.abc = "def"
// d2 : Demo = Demo(abc)
// d1 : Demo = Demo(def)
A more Scala idiomatic way would be to use immutable case classes:
case class Person(name: String, age: Int)
val bob = Person("Bob", 30)
val joe = bob.copy(name="Joe")
// bob : Person = Person(Bob,30)
// joe : Person = Person(Joe,30)
afaik, case classes are meant to be immutable.
case classes are cheap, so an immutable case class might meet your requirements.
If you change a mutable object, a reference to that object will not help you to store a previous state. For that you would need a copy of that object.
So, depending on your requirements, I'd do either copy the original object changing select attributes to qasi mutate it
case class Demo(abc: String)
val demo = Demo("foo")
val quasiMutatedDemo = demo.copy(abc = "bar")
or I would have to implement a copy in my mutable class (just because I just could never bring myself to create a mutable case class).
class Demo(var abc: String) {
def copy: Demo = new Demo(this.abc)
}
val demo = new Demo("foo")
val odlDemo = demo.copy
val mutatedDemo = demo.abc = "bar"
The best way to achieve this is to not use mutable variables. Like
case class Demo(abc: String)
val d = Demo("bob")
val newDemo = d.copy(abc = "joe")
println("old name is " + d.abc + " the new name is " + newDemo.abc)