How to use scala class member name as variable - scala

In scala, is there a way to access a class member,but the member name is a var?
Below is a code snippet, there is a class member called "one_star". I have a var whose value is "one_star", and I want use this var as the member name of the "case class".
case class Features(
// star about
var one_star: String = "0",
var two_star: String = "0",
var three_star: String = "0",
var four_star: String = "0",
var five_star: String = "0"
// other about
)
object Features {
def apply(): Features = {
val features = new Features()
var testVar = "one_star"
features.${testVar} = "1"
features
}
}

If you want to change field name dynamically, i.e. provide class variable name as value, find field that match given variable name and finally change the value for that field, there are several ways: the simple one is to use pattern match to check the field value and change instance value by yourself and return instance. However, it can be quite messy as you need to handle for every fields defined in your class and in case you have many fields, it can be quite cumbersome. Therefore, you will need some generic way to solve this problem.
Another approach is to use scala reflection. Scala reflection is designed for this, i.e. modifying your codes at runtime like your case and in more generic way. Following is a code snippet that change value of your instance for given field name.
case class Features(
// star about
var one_star: String = "0",
var two_star: String = "0",
var three_star: String = "0",
var four_star: String = "0"
// other about
) {
def copyInstance(variableName: String, value: String): Features = {
// 1. Initialize Features instance
val instance = Features()
// 2. Import scala reflection api.
import scala.reflect.runtime.{universe => ru}
// 3. Get the mirror of instance of Features class.
// Mirror will reflect to instance of Features case class, and we will use this instance mirror to modify its fields value.
val instanceMirror = ru.runtimeMirror(instance.getClass.getClassLoader).reflect(instance)
// 4. Now, Get the field whose value need to be changed - i.e. name you provide - variableName.
val field = ru.typeOf[Features].decl(ru.TermName(variableName)).asTerm
// 5. Get the mirror for that field so that we can read and write to this field.
val fieldMirror = instanceMirror.reflectField(field)
// 6. Finally, set the value to this field.
fieldMirror.set(value)
// 7. Return the changed instance.
instance
}
}
val features = Features()
val changedFeatures = features.copyInstance("one_star", "changed")
println(features)
println(changedFeatures)
//Result
Features(0,0,0,0)
Features(changed,0,0,0)
Also, note that you may need to handle the Exception for cases where invalid variable name and value is provided. In addition, if your case class contains >22 field parameters, certain features of case class are not available.

Scala is static type language and doesn't allow these language constructions. But you can use reflection (hard way) or pattern matching with code like this one (simple way):
class Features (
var one_star: String = "0",
var two_star: String = "0",
var three_star: String = "0",
var four_star: String = "0",
var five_star: String = "0") {
def setField(field: String, value: String): Unit = {
field match {
case "one_star" => one_star = value
case "two_star" => two_star = value
case "three_star" => three_star = value
case "four_star" => four_star = value
case "five_star" => five_star = value
}
}
}

This is possible using scala-reflect, although under most circumstances I would not recommend it.
import scala.reflect.runtime.universe._
val field = typeOf[Features].decl(TermName(testVar)).asTerm.accessed.asTerm
val mirror = runtimeMirror(classOf[Features].getClassLoader)
mirror.reflect(features).reflectField(field).set("1")
Are you sure you don't want to use or extend Map[String, String] for your class? So many properties is not typical.

Related

Scala proper way to initialize ones changed in runtime fields: placeholder/null, None or zero element?

I got class with fields which value at initialization is unknown. But after, in runtime that values is will gained and setted to fields just ones.
I want to decide what first initialization is best to use for that fields. As i read, there are such methods:
using placeholder _ or null [Bad way]:
var name: String = _
var nextUser: User = null
using None, and than in my code Some(v)[Good, but verbose]:
var name: Option[String] = None
var nextUser: Option[User] = None
using "zero" element:
var name: String = ""
var nextUser: User = new User()
using stub:
var name: String = "undefined"
var nextUser: User = UndefinedUser
I see 3 problems:
it is verbose to get values from Some() writing any times .get or using match/case
it is bad to use var for field which really will be setted by value just one time, but in runtime
it is bad to write updateUser-like methods
Now i am using None in that fields, because for some types, which is not in my library, is no any constructor or empty\"zero" value:
class ClassWithLazyFields {
var name: String = ""
var age: Int = 0
//here, after first asignment as `None` i will set it values only one time
var myThread: Option[Thread] = None
var nextUser: Option[User] = None
var myTransformUnit: Option[TransformUnit] = None
def updateUser(u: User): Unit = {
nextUser = u
}
}
//after first asignment as `None` i set nextUser value like that
classInstance.updateUser(userCat)
// bad ".get" in callings
val name = classInstance.myThread.get.name
val hoursToStart = classInstance.myTransformUnit.get.waitTime.hours
// or more verbose match/case
val hoursToStart = classInstance.myTransformUnit match {
case Some(v) => v.waitTime.hours
case None => 0
}
What you can advice to me?
I need something like lazy var or any good advice.
The advice is to avoid using mutable data structures like this in the first place.
Make the class immutable and change methods like updateUser to return a new updated instance rather than modifying the current instance.
But if you must do this, Option is specifically designed for cases where values may or may not be present. Methods like map and getOrElse make it easy (and safe) to use Option values with very little overhead.
For example, this is how you safely calculate name and hoursToStart:
val name = classInstance.myThread.fold("NoName")(_.name)
val hoursToStart = classInstance.myTransformUnit.fold(0)(_.waitTime.hours)
If you want to use multiple Option values, use for like this:
for {
thread <- classInstance.myThread
user <- classInstance.nextUser
unit <- classInstance.myTransformUnit
} {
// Code that uses thread, user, and unit
}
The code will only be called if all three values are not None.

Scala : How to pass a class field into a method

I'm new to Scala and attempting to do some data analysis.
I have a CSV files with a few headers - lets say item no., item type, month, items sold.
I have made an Item class with the fields of the headers.
I split the CSV into a list with each iteration of the list being a row of the CSV file being represented by the Item class.
I am attempting to make a method that will create maps based off of the parameter I send in. For example if I want to group the items sold by month, or by item type. However I am struggling to send the Item.field into a method.
F.e what I am attempting is something like:
makemaps(Item.month);
makemaps(Item.itemtype);
def makemaps(Item.field):
if (item.field==Item.month){}
else (if item.field==Item.itemType){}
However my logic for this appears to be wrong. Any ideas?
def makeMap[T](items: Iterable[Item])(extractKey: Item => T): Map[T, Iterable[Item]] =
items.groupBy(extractKey)
So given this example Item class:
case class Item(month: String, itemType: String, quantity: Int, description: String)
You could have (I believe the type ascriptions are mandatory):
val byMonth = makeMap[String](items)(_.month)
val byType = makeMap[String](items)(_.itemType)
val byQuantity = makeMap[Int](items)(_.quantity)
val byDescription = makeMap[String](items)(_.description)
Note that _.month, for instance, creates a function taking an Item which results in the String contained in the month field (simplifying a little).
You could, if so inclined, save the functions used for extracting keys in the companion object:
object Item {
val month: Item => String = _.month
val itemType: Item => String = _.itemType
val quantity: Item => Int = _.quantity
val description: Item => String = _.description
// Allows us to determine if using a predefined extractor or using an ad hoc one
val extractors: Set[Item => Any] = Set(month, itemType, quantity, description)
}
Then you can pass those around like so:
val byMonth = makeMap[String](items)(Item.month)
The only real change semantically is that you explicitly avoid possible extra construction of lambdas at runtime, at the cost of having the lambdas stick around in memory the whole time. A fringe benefit is that you might be able to cache the maps by extractor if you're sure that the source Items never change: for lambdas, equality is reference equality. This might be particularly useful if you have some class representing the collection of Items as opposed to just using a standard collection, like so:
object Items {
def makeMap[T](items: Iterable[Item])(extractKey: Item => T): Map[T,
Iterable[Item]] =
items.groupBy(extractKey)
}
class Items(val underlying: immutable.Seq[Item]) {
def makeMap[T](extractKey: Item => T): Map[T, Iterable[Item]] =
if (Item.extractors.contains(extractKey)) {
if (extractKey == Item.month) groupedByMonth.asInstanceOf[Map[T, Iterable[Item]]]
else if (extractKey == Item.itemType) groupedByItemType.asInstanceOf[Map[T, Iterable[Item]]]
else if (extractKey == Item.quantity) groupedByQuantity.asInstanceOf[Map[T, Iterable[Item]]]
else if (extractKey == Item.description) groupedByDescription.asInstanceOf[Map[T, Iterable[Item]]]
else throw new AssertionError("Shouldn't happen!")
} else {
Items.makeMap(underlying)(extractKey)
}
lazy val groupedByMonth = Items.makeMap[String](underlying)(Item.month)
lazy val groupedByItemType = Items.makeMap[String](underlying)(Item.itemType)
lazy val groupedByQuantity = Items.makeMap[Int](underlying)(Item.quantity)
lazy val groupedByDescription = Items.makeMap[String](underlying)(Item.description)
}
(that is almost certainly a personal record for asInstanceOfs in a small block of code... I'm not sure if I should be proud or ashamed of this snippet)

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.

Scala: How to define an enum with extra attributes?

I have a use-case where I need to define a new enum type LongShort but I need it in a way to also carry the sign so it can be directly used in mathematical expressions e.g.
object LongShortType extends Enumeration {
type Type = Value
val Long = Value(+1)
val Short = Value(-1)
}
I'd then like to use it like this:
val longShort = LongShortType.Short
val numberOfContracts: Int = 10
val vanillaOptionNotional: Double = longShort*numberOfContracts
but this leads to compiler error cannot resolve symbol * ... is there a way to extract the value of the enum? Or am I not understanding how enum types work?
The type of LongShortType.Short isn't Int, it's Value. You can either extract the underlying id of the value:
val longShort = LongShortType.Short.id
Which is a little ugly. Or you could not use an enum type at all:
object LongShortType {
val Long = 1
val Short = -1
}
And then your equation would work as is.
OK I worked out a solution to accomplish what I wanted without any compromisse and by that I mean that this solution has all the advantages of using Scala enum e.g. the withName and still allows me to define extra features on it:
object LongShortType extends Enumeration {
type Type = LongShortVal
val Long = Value("Long", +1)
val Short = Value("Short", -1)
case class LongShortVal(name: String, sign: Int) extends Val(nextId, name)
protected final def Value(name: String, sign: Int) = new LongShortVal(name, sign)
}
and now can do:
val longShort = LongShortType.Short
val numberOfContracts: Int = 10
val vanillaOptionNotional: Double = longShort.sign*numberOfContracts
and can also do:
val longShort = LongShort.withName("Long") // returns LongShort.Long

Scala Properties Question

I'm still learning Scala, but one thing I thought was interesting is that Scala blurs the line between methods and fields. For instance, I can build a class like this...
class MutableNumber(var value: Int)
The key here is that the var in the constructor-argument automatically allows me to use the 'value' field like a getter/setter in java.
// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)
If I want to add constraints, I can do so by switching to using methods in place of the instance-fields:
// require all mutable numbers to be >= 0
class MutableNumber(private var _value: Int) {
require(_value >= 0)
def value: Int = _value
def value_=(other: Int) {
require(other >=0)
_value = other
}
}
The client side code doesn't break since the API doesn't change:
// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)
My hang-up is with the named-parameter feature that was added to Scala-2.8. If I use named-parameters, my API does change and it does break the api.
val num = new MutableNumber(value=5) // old API
val num = new MutableNumber(_value=5) // new API
num.value = 6
println(num.value)
Is there any elegant solution to this? How should I design my MutableNumber class so that I can add constraints later on without breaking the API?
Thanks!
You can use the same trick that case classes do: use a companion object.
object Example {
class MutableNumber private (private var _value: Int) {
require (_value >= 0)
def value: Int = _value
def value_=(i: Int) { require (i>=0); _value = i }
override def toString = "mutable " + _value
}
object MutableNumber {
def apply(value: Int = 0) = new MutableNumber(value)
}
}
And here it is working (and demonstrating that, as constructed, you must use the object for creations, since the constructor is marked private):
scala> new Example.MutableNumber(5)
<console>:10: error: constructor MutableNumber cannot be accessed in object $iw
new Example.MutableNumber(5)
^
scala> Example.MutableNumber(value = 2)
res0: Example.MutableNumber = mutable 2
scala> Example.MutableNumber()
res1: Example.MutableNumber = mutable 0
Thanks for the answer! As an aside, I think the Scala-guys might be aware that there's an issue:
What's New in Scala 2.8: Named and Default Parameters
...
Until now, the names of arguments were a somewhat arbitrary choice for library developers, and weren't considered an important part of the API. This has suddenly changed, so that a method call to mkString(sep = " ") will fail to compile if the argument sep were renamed to separator in a later version.
Scala 2.9 implements a neat solution to this problem, but while we're waiting for that, be cautious about referring to arguments by name if their names may change in the future.
http://www.artima.com/scalazine/articles/named_and_default_parameters_in_scala.html
class MutableNumber {
private var _value = 0 //needs to be initialized
def value: Int = _value
def value_=(other: Int) {
require(other >=0) //this requirement was two times there
_value = other
}
}
you can modify all members of any class within curly braces
val n = new MutableNumber{value = 17}