4

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?

2
  • Not sure if I get it right, but you might want to look into dynamic Commented May 31, 2019 at 20:49
  • 1
    What is the use case for this? Don't try to write Scala as if it were JavaScript. Commented Jun 1, 2019 at 9:47

3 Answers 3

6

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:

  • This is limited to case classes.
  • This returns an 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).
  • You have to specify the return type of the associated value.
Sign up to request clarification or add additional context in comments.

Comments

1

No. I don't think so. As Scala based on JVM which require static type. Unless you want to use reflection

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.