If I had a
case class User(var firstName: String, var lastName: String, var city: String)
and a list
val users = List(
new User("Peter", "Fox", "Berlin"),
new User("Otto", "Schmidt", "Berlin"),
new User("Carl", "Schmidt", "Berlin"),
new User("Hans", "Schmidt", "Berlin"),
new User("Hugo", "Schmidt", "Berlin"))
define something
val test1 = (user:User,key:String) => user.lastName.equals(key)
val test2 = (user:User,key:String) => user.firstName.startsWith(key)
and filter
val test = users.filter(u => {
test1(u,"Schmidt") && test2(u,"H")
})
This works fine. But How can I generate something that filters test1, test2 ... testn dynamicly form maybe a list? I want to have a lot of filter conditions predefined and combine them to one condition (like test1(u,"Schmidt") && test2(u,"H")) to filter my list and combine the order of filtering.
Basically what you want is a way to compose predicates. A predicate in this case is a function that takes an User and returns a Boolean. So the type is just User => Boolean
First of all, I would reformulate your "predicate generator methods" so that you can generate predicates. Inside some helper object:
object UserPredicates {
def lastNameEquals(value:String)(user:User) = user.lastName == value
def firstNameStartsWith(value:String)(user:User) = user.firstName.startsWith(value)
..
}
To generate the predicate firstName starts with "H", you can partially apply the firstNameStartsWith method like so:
import UserPredicates._
val p1: User => Boolean = firstNameStartsWith("H")
Then it is pretty simple to create a predicate from multiple predicates by defining methods that compose predicates. Maybe also inside UserPredicates:
def and(predicates:Seq[User => Boolean])(user:User) = predicates.forall(predicate => predicate(user))
def or(predicates:Seq[User => Boolean)(user:User) = predicates.exists(predicate => predicate(user))
Then you can do
import UserPredicates._
val condition1 = firstNameStartsWith("H")
val condition2 = lastNameEquals("Schmidt")
val combined = and(Seq(condition1, condition2))
users.filter(combined)
Or short
users.filter(and(firstNameStartsWith("H"), lastNameEquals("Schmidt")))
.
By the way: you should not use new to create case class instances. Also, you don't have to use equals to compare strings. The scala == operator will call equals and not just check for reference equality like the java == operator.
I kind of like using implicits so you can get this kind of syntax:
case class User(var firstName: String, var lastName: String, var city: String)
val users = List(
new User("Peter", "Fox", "Berlin"),
new User("Otto", "Schmidt", "Berlin"),
new User("Carl", "Schmidt", "Berlin"),
new User("Hans", "Schmidt", "Berlin"),
new User("Hugo", "Schmidt", "Berlin"))
//Note that these are curried now
val filterLastName = (key: String) => (user: User) => user.lastName.equals(key)
val filterFirstNameFirstChars = (key: String) => (user: User) => user.firstName.startsWith(key)
implicit class FilterHelper[A](l: List[A]) {
def filter(filters: List[A => Boolean]): List[A] = {
l.filter(a => filters.forall(f => f(a)))
}
}
//implicit filter takes a list of user predicates
val test = users.filter(List(
filterLastName("Schmidt"),
filterFirstNameFirstChars("H")))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With