Arash Shahkar
Arash Shahkar

Reputation: 655

Scala: Dynamic selection criteria as a method argument

My intention is to create a function in Scala which accepts some kind of a dynamic query (similar to case expressions in pattern matching) and returns matching instances inside a List. Suppose that the list consists of instances of case class Person which has few properties with different types. The function should be able to accept a dynamic combination of values for some of the fields, and return matching Persons. I am specifically looking for a clean solution. One possible ways to use such a function would be to pass an object with an anonymous type as the query (the "pattern"):

def find(?): List<Person> = { ? }

val matches = find(new { val name = "Name"; val gender = Gender.MALE })

Ideally, I would like to develop a clean way of passing conditions, instead of concrete values, but it's not essential. Since I am learning Scala, I am not aware of all the techniques to implement such a thing. In C#, I used an anonymous type (similar to the second line of code above) and dynamic parameters to achieve something similar. What is a clean and elegant solution in Scala?

Upvotes: 1

Views: 745

Answers (1)

makingthematrix
makingthematrix

Reputation: 395

I'm not sure if this is what you are looking for but let's try it this way:

First, we define Person as case class Person(name: String, gender: Gender.Value) where Gender is an already defined enum.

Then we create a Query case class which has the same fields, but as options which default to None, and a method for comparing the query to a person:

case class Query(name: Option[String] = None, 
                 gender: Option[Gender.Value] = None){

  def ===(person: Person) = check(person.name, name) && 
                            check(person.gender, gender)

  private def check[T](field: T, q: Option[T]) = field == q.getOrElse(field)
}

Unfortunately, in this solution === has to call check separately for each field. Let's leave it like that for now. Maybe it is sufficient (because, for example, the list of fields will not change).

Note that check returns true if the query's option is None, sot you don't have to pass all fields of the query:

val q = Query(name = Some("Ann")) // the gender is not important
q === Person("Ann", Gender.FEMALE) // returns true

And finally the find method:

def find(people: List[Person], query: Query) = people.filter(query === _)

Upvotes: 1

Related Questions