Scala class: avoid parameters while creating new instance - scala

Suppose I have 2 files.
myMainApplication.scala
printInfos.scala
Briefly myMainApplication.scala contains
val name = "blah"
val age= 45
Briefly my class myMainApplication.scala should print out the name and the age by creating a new printInfos instance. However I should not add any parameters while creating the instance.
What I would like to AVOID:
Inside printInfos.scala
class printInfos(val myName: String,
val myAge: Int
val myLoc: String
val myNPP: Double
val myCountry: String
val myProvice: String
val myNPAP: String) {
def printInfos() = {
println("Your method printInfos print out" + myName + myAge + myLoc + myNPP + myCountry + myProvice + myNPAP)
}
}
Inside myMainApplication.scala
val name = "blah"
val age= 45
val loc = "blah"
val npp = 45.5
val country = "germany"
val province = "bayern"
val npap = "blaha"
// want to avoid to enter so many params
val printInfoInstance = new printInfos(name, age, loc, npp, country, province, npap)
printInfoInstance.printInfos()
I would like to get something similar:
Inside printInfos.scala
class printInfos() {
var myName: String = 0
var myAge: Int = 0
var myLoc: String = 0
var myNPP: Double = 0
var myCountry: String = 0
var myProvice: String = 0
var myNPAP: String = 0
def printInfos() = {
println("Your method printInfos print out" + myName + myAge + myLoc + myNPP + myCountry + myProvice + myNPAP)
}
}
Inside myMainApplication.scala
// want to create new instance with no params at begining
val printInfoInstance = new printInfos()
val name = "blah"
printInfoInstance.myName() = name
val age= 45
printInfoInstance.myAge() = age
val loc = "blah"
printInfoInstance.myLoc() = loc
val npp = 45.5
printInfoInstance.myNPP() = npp
val country = "germany"
printInfoInstance.myCountry() = country
val province = "bayern"
printInfoInstance.myProvice() = province
val npap = "blaha"
printInfoInstance.myNPAP() = npap
printInfoInstance.printInfos
Looking for improvements
Would it be possible to get something similar to the second proposal but avoiding to use var ? However, the final aim is still to avoid adding a lot of parameters when creating a new intance.

I'd suggest you use scala's case classes for this. It provides a copy method that allows you to create a new instance with the changed parameters. This keeps everything immutable. Note that you don't have to update all of the parameters - you could just do one at a time.
case class printInfos(myName: String = "",
myAge: Int = 0,
myLoc: String = "",
myNPP: Double = 0,
myCountry: String = "",
myProvince: String = "",
myNPAP: String = "") {
def printInfos() = {
println("Your method printInfos print out" + myName + myAge + myLoc + myNPP + myCountry + myProvince + myNPAP)
}
}
val printInfoInstance = new printInfos()
val name = "blah"
val age= 45
val loc = "blah"
val npp = 45.5
val country = "germany"
val province = "bayern"
val npap = "blaha"
val newInstance = printInfoInstance.copy(
myName = name,
myAge = age,
myLoc = loc,
myNPP = npp,
myCountry = country,
myProvince = province,
myNPAP = npap
)
newInstance.printInfos()

In addition to what #longshorej has mentioned, provided you have a case class defined. you can instantiate without the new keyword:
printInfos("name","age","loc","npp","country","province","npap") would create a new instance.
For the printing part I'd suggest overriding toString so that it aligns well with standard nomenclature.

It is a benefit of the type system to be able to require all the parameters needed to create a valid instance of a class.
If you want to avoid the verbosity of passing all the parameters every time, you can provide default values for them (only a good idea if they are truly optional, as you really want to avoid invalid intermediate states).
// assuming it doesn't make sense to instantiate without
// a few required parameters
case class InfoPrinter (
name: String, // required
age: Int, // required
loc: String = "",
NPP: Double = 0,
country: String = "",
province: String = "",
NPAP: String = ""
) {
def printInfos =
println(s"Your method printInfos print out $name $age $loc $NPP $country $province $NPAP")
}
You can then use it like this:
// named params or positionally
InfoPrinter("blah", 45, country="germany", province="bayern").printInfos
Any parameters with default values can be omitted.

Related

Join two strings in Scala with one to one mapping

I have two strings in Scala
Input 1 : "a,c,e,g,i,k"
Input 2 : "b,d,f,h,j,l"
How do I join the two Strings in Scala?
Required output = "ab,cd,ef,gh,ij,kl"
I tried something like:
var columnNameSetOne:Array[String] = Array(); //v1 = "a,c,e,g,i,k"
var columnNameSetTwo:Array[String] = Array(); //v2 = "b,d,f,h,j,l"
After I get the input data as mentioned above
columnNameSetOne = v1.split(",")
columnNameSetTwo = v2.split(",");
val newColumnSet = IntStream.range(0, Math.min(columnNameSetOne.length, columnNameSetTwo.length)).mapToObj(j => (columnNameSetOne(j) + columnNameSetTwo(j))).collect(Collectors.joining(","));
println(newColumnSet)
But I am getting error on j
Also, I am not sure if this would work!
object Solution1 extends App {
val input1 = "a,c,e,g,i,k"
val input2 = "b,d,f,h,j,l"
val i1= input1.split(",")
val i2 = input2.split(",")
val x =i1.zipAll(i2, "", "").map{
case (a,b)=> a + b
}
println(x.mkString(","))
}
//output : ab,cd,ef,gh,ij,kl
Easy to do using zip function on list.
val v1 = "a,c,e,g,i,k"
val v2 = "b,d,f,h,j,l"
val list1 = v1.split(",").toList
val list2 = v2.split(",").toList
list1.zip(list2).mkString(",") // res0: String = (a,b),( c,d),( e,f),( g,h),( i,j),( k,l)

scala forward reference extends over definition of value dataframe

I'm trying to typecast the columns in the data frame df_trial which has all the columns as string, based on an XML file I'm trying to type cast each column.
val columnList = sXml \\ "COLUMNS" \ "COLUMN"
val df_trial = sqlContext.createDataFrame(rowRDD, schema_allString)
columnList.foreach(i => {
var columnName = (i \\ "#ID").text.toLowerCase()
var dataType = (i \\ "#DATA_TYPE").text.toLowerCase()
if (dataType == "number") {
print("number")
var DATA_PRECISION: Int = (i \\ "#DATA_PRECISION").text.toLowerCase().toInt
var DATA_SCALE: Int = (i \\ "#DATA_SCALE").text.toLowerCase().toInt;
var decimalvalue = "decimal(" + DATA_PRECISION + "," + DATA_SCALE + ")"
val df_intermediate: DataFrame =
df_trial.withColumn(s"$columnName",
col(s"$columnName").cast(s"$decimalvalue"))
val df_trial: DataFrame = df_intermediate
} else if (dataType == "varchar2") {
print("varchar")
var DATA_LENGTH = (i \\ "#DATA_LENGTH").text.toLowerCase().toInt;
var varcharvalue = "varchar(" + DATA_LENGTH + ")"
val df_intermediate =
df_trial.withColumn(s"$columnName",
col(s"$columnName").cast(s"$varcharvalue"))
val df_trial: DataFrame = df_intermediate
} else if (dataType == "timestamp") {
print("time")
val df_intermediate =
df_trial.withColumn(s"$columnName", col(s"$columnName").cast("timestamp"))
val df_trial: DataFrame = df_intermediate
}
});
In each branch of the if-else you're using the values called df_trial before you've defined them. You'll need to rearrange the code to define them first.
Note: the way you have it, the df_trial at the very top is not being used. Depending on what you are trying to do, you may want to change the first df_trial to a var and remove the val from the other usages. (This is probably still wrong since you will be overwriting the same variable multiple times as you loop over columnList).

Scala 2.11.8, OS:Windows 7, Java: JDK1.8

I am creating a Companion Objects, How do i traverse these objects?, i have written but not working, error thrown
Please help here
scala> :paste
object Network {
class Member(val name: String) {
var strName = name
val contacts = new collection.mutable.ArrayBuffer[Member]
println(" name -->" + strName)
}
}
class Network {
private val members = new collection.mutable.ArrayBuffer[Network.Member]
def join(name: String) = {
val m = new Network.Member(name)
members += m
m
}
}
val chatter = new Network
val myFace = new Network
val fred = chatter.join("Fred")
val wilma = chatter.join("Wilma")
fred.contacts += wilma // OK
val barney = myFace.join("Barney") // Has type myFace.Member
fred.contacts += barney // allowed
How do i traverse these objects?, i have written but not working, error thrown
for (a<- fred.contacts){
var Network.Member m = a
println("m -->" + m.strName)
//println("m -->" + a)
}
The declaration of m variable is not correct.
var m:Network.Member = a
That's the correct way to declare a variable in Scala. Or you can just ignore the type and let Scala interpret it.
var m = a

Using scala constants in constant expressions

I have constants, that are made of other, smaller constants. For example
object MyConstants {
final val TABLENAME = "table_name";
final val FIELDNAME = "field_name";
final val INDEXNAME = TABLENAME + "_" + FIELDNAME + "_ix"; // this one does not want to be constant
}
I want these to be true constants because I use them in annotations.
How do I make it work? (on scala 2.11)
What I want is
interface MyConstants {
String TABLENAME = "table_name";
String FIELDNAME = "field_name";
String INDEXNAME = TABLENAME + "_" + FIELDNAME + "_ix";
}
but in scala. Scalac does not pick up constants for usage in annotations if it compiles them from java class/interface (see SI-5333) so I decided to put them in a scala object. It works for literals, and for expressions with literals, but not for expressions with other constants.
With this code:
import javax.persistence.Entity
import javax.persistence.Table
import org.hibernate.annotations.Index
object MyConstants {
final val TABLENAME = "table_name";
final val FIELDNAME = "field_name";
final val INDEXNAME = TABLENAME + "_" + FIELDNAME + "_ix";
}
#Entity
#Table(name = MyConstants.TABLENAME)
#org.hibernate.annotations.Table(
appliesTo = MyConstants.TABLENAME,
indexes = Array(new Index(name = MyConstants.INDEXNAME, columnNames = Array(MyConstants.FIELDNAME))))
class MyEntity {
}
I get a following error on line indexes = ...
annotation argument needs to be a constant; found: MyConstants.INDEXNAME
Edit:
After fiddling around with a few build configurations, I think this is actually a scala-ide specific issue. The code does indeed compile alright when I build project with gradle or sbt. I do use build tool for my actual projects, so in the end it's about having a few incomprehensible markers in the IDE - annoying, but has little to do with functionality.
I used constants in JPA with scala. This code compiles, and I used it:
FreeDays.scala
#Entity
#Table(name = "free_days")
#NamedQueries(
Array(
new NamedQuery(name = JpaQueries.IS_FREE_DAYS, query = "SELECT f FROM FreeDays f WHERE f.dateOfFreeDay = :" + JpaQueries.DATE)
)
)
class FreeDays {
def this(id: Int, name: String, dateOfFreeDay: Date) = {
this()
this.id = id
this.name = name
this.dateOfFreeDay = dateOfFreeDay
}
#Id
#GeneratedValue
var id: Long = _
var name: String = _
#Column(name = "date_of_free_day")
#Temporal(TemporalType.DATE)
var dateOfFreeDay: Date = _
}
JpaQueries.scala
object JpaQueries extends JpaQueries
sealed trait JpaQueries {
final val IS_FREE_DAYS = "IS_FREE_DAYS"
final val DATE = "date"
}

Is there an better alternative to implement Builder Pattern in Scala?

I have to create an instance of class BenchmarkOption based on the command line arguments. I certainly use pojo style, but this is not immutable. So I use Builder Pattern of Java style. Here is the class I implement :
object CommandLineHelper {
//TODO: use configuration file ?
val MILLI_SEC_SEPERATORS = 0 + "," + Int.MaxValue
val SBE_ID = "SBETEST5";
val SBE_PSW = "GZ#API53";
val NUM_OF_REQUESTS = 1
val NUM_OF_WORKERS = 1
val PAUSE_IN_MILlI_SECOND = 1L;
}
class BenchmarkOption private {
import com.ggd543.mulerestletdemo.{CommandLineHelper => CLH}
private var _msSeperators = CommandLineHelper.MILLI_SEC_SEPERATORS
def msSeperators = _msSeperators
private var _nOfReq = CLH.NUM_OF_REQUESTS
def nOfReq = _nOfReq
private var _sbeId = CLH.SBE_ID
def sbeId = _sbeId
private var _sbePsw = CLH.SBE_PSW
def sbePsw = _sbePsw
private var _pauseInMilliSec = CLH.PAUSE_IN_MILlI_SECOND;
def pauseInMillSec = _pauseInMilliSec
private var _dataFile = new File("./data.csv")
def dataFile = _dataFile
// may be too many fields
}
object BenchmarkOption {
def newBuilder() = new Builder
class Builder {
private val bmo = new BenchmarkOption
def buildBenchmarkOption = bmo;
def msSeperators_=(s: String) = bmo._msSeperators = s
def msSeperators = bmo._msSeperators
def nOfReq_=(n: Int ) = bmo._nOfReq = n
def nOfReq = bmo._nOfReq
def sbeId_=(s: String) = bmo._sbeId = s
def sbeId = bmo._sbeId
def sbePsw_=(s: String ) = bmo._sbePsw = s
def sbePsw = bmo._sbePsw
def pauseInMilliSec_=(milliSec: Long) = bmo._pauseInMilliSec = milliSec
def pauseInMilliSec = bmo._pauseInMilliSec
def dataFile_=(file: File) = bmo._dataFile = file
def dataFile = bmo._dataFile
}
}
As you can see that the code is lengthy and not good at reading. I think there is an alternative to rewrite it . Any suggestion ?
The Builder Pattern is built into Scala. Just use named constructor arguments and default values whenever possible. If there are too many arguments, then rethink your design. I bet there are plenty opportunities to group them somehow into proper data structures, thus reducing the number of constructor arguments.
I don't see why you can' just use constructor arguments - are all parameters not known from the start?
How about this ?
class BenchmarkOption private {
protected val instance = new {
var msSeperators = CommandLineHelper.MILLI_SEC_SEPERATORS
var nOfReq = CLH.NUM_OF_REQUESTS
var sbeId = CLH.SBE_ID
var sbePsw = CLH.SBE_PSW
var pauseInMilliSec = CLH.PAUSE_IN_MILlI_SECOND;
var dataFile = new File("./data.csv" )
def buildInstance() = BenchmarkOption.this;
}
def msSeperators = instance.msSeperators
def nOfReq = instance.nOfReq
def sbeId = instance.sbeId
def sbePsw = instance.sbePsw
def pauseInMilliSec = instance.pauseInMilliSec
def dataFile = instance.dataFile
}
object BenchmarkOption {
def newBuilder() = new BenchmarkOption{}.instance
}