In ES6 we can refer to a field of an object as:
seller['firstname']
to represent
seller.firstname
Is there a way to do the same in Scala? I mean, to use strings to refer to object fields?
In ES6 we can refer to a field of an object as:
seller['firstname']
to represent
seller.firstname
Is there a way to do the same in Scala? I mean, to use strings to refer to object fields?
With Scala 2.13, you should be able to do something more or less related by using case classes' new productElementNames method which returns an iterator over its field names.
By zipping field names with field values obtained with productIterator we can access a case class field value by using the string version of the field name:
implicit class CaseClassExtensions(obj: Product) {
def select[T](field: String): Option[T] =
(obj.productElementNames zip obj.productIterator)
.collectFirst { case (`field`, value) => value.asInstanceOf[T] }
}
// case class Seller(firstName: String, lastName: String)
Seller("Hello", "World").select[String]("firstName")
// Option[String] = Some(Hello)
Here, the implicit class is used to enrich the Product type (which is case class inherited type) in order to call this select method on any case class.
But contrary to what you're used to in Javascript:
Option of the value (or you'd have to deal with exceptions when trying to get the value of a field which isn't defined).No. I don't think so. As Scala based on JVM which require static type. Unless you want to use reflection
The solution from Xavier Guihot would be working from Scala 2.13, but you could also implement by-string-access with shapeless with Scala 2.12 or below.
First of all, add shapeless to your build.sbt:
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.3"
We'd need to use LabelledGeneric and ToMap. We could implement it as an extension class working on case classes:
object DynamicAccess {
implicit class DynamicAccessOps[R <: HList, T <: Product with Serializable](o: T)(
implicit gen: LabelledGeneric.Aux[T, R],
toMap: ToMap[R]
) {
private lazy val fields = toMap(gen.to(o))
def apply[S](field: String): Option[S] = fields
.get(Symbol.apply(field).asInstanceOf[toMap.Key])
.asInstanceOf[Option[S]]
}
}
Then you could use it like this:
import DynamicAccess._
case class Foo(bar: String, baz: String)
val f = Foo("x", "y")
f[String]("bar") // Some(x)
f[String]("baz") // Some(y)
f[String]("foobar") //None