필터

2013-07-22 3 views
2

나는필터

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")) 

정의가 있다면

val test1 = (user:User,key:String) => user.lastName.equals(key) 
val test2 = (user:User,key:String) => user.firstName.startsWith(key) 

및 필터링

val test = users.filter(u => { 
    test1(u,"Schmidt") && test2(u,"H") 
}) 

이 작동합니다. 하지만 어떻게 test1, test2 ... 필터 동적으로 아마 목록을 형성 뭔가를 생성 할 수 있습니까? 미리 정의 된 많은 필터 조건을 갖고 싶습니다. 내 목록을 필터링하고 필터링 순서를 결합하려면 test1 (u, "Schmidt") & & test2 (u, "H")와 같은 조건을 조합하십시오.

답변

4

기본적으로 원하는 것은 술어를 작성하는 방법입니다. 이 경우의 술어는 User를 취해 Boolean 값을 반환하는 함수입니다. 그래서 형식은 User => Boolean입니다.

우선, "predicate generator methods"를 다시 작성하여 조건자를 생성 할 수 있습니다. 일부 도우미 개체 내부 :

object UserPredicates { 
    def lastNameEquals(value:String)(user:User) = user.lastName == value 
    def firstNameStartsWith(value:String)(user:User) = user.firstName.startsWith(value) 
    .. 
} 

이 firstName을이 "H"로 시작하는 술어를 생성하려면, 당신은 부분적으로 그렇게 같은 firstNameStartsWith 방법을 적용 할 수

import UserPredicates._ 
val p1: User => Boolean = firstNameStartsWith("H") 

를 그런 다음에서 술어를 만드는 아주 간단합니다 술어를 작성하는 메소드를 정의하여 다중 술어. 어쩌면 또한 내부 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)) 

는 그런 다음

import UserPredicates._ 
val condition1 = firstNameStartsWith("H") 
val condition2 = lastNameEquals("Schmidt") 
val combined = and(Seq(condition1, condition2)) 
users.filter(combined) 

또는 짧은

users.filter(and(firstNameStartsWith("H"), lastNameEquals("Schmidt"))) 

을 할 수 있습니다.

그건 그렇고 : 당신은 case 클래스 인스턴스를 만들기 위해 new를 사용하면 안됩니다. 또한 문자열을 비교하는 데 equals를 사용할 필요가 없습니다. scala == 연산자는 java == 연산자와 같은 참조 동등성을 검사하는 것이 아니라 equals를 호출합니다.

+0

을 'Seq'를 여기에서 제거하십시오 :'and (condition1, condition2)' –

+0

알아요. 또한 predicates.exists (predicate => predicate (user)) 대신 predicates.exists (_ (user))를 사용하여 결합 술어를 단순화 할 수 있습니다. 그러나 좀 더 장황한 것이 더 좋을 것이라고 생각했습니다. 그렇지 않으면 초보자에게 혼란을 일으킬 수 있습니다. –

+0

'UserPredicates'를 만들지 않고'User' methods'와 (_. firstName.firstNameStartsWith ("H"), ...)'를 사용하지 않는 것이 궁금합니다. –

1

I 종류의 당신이 구문의 종류 얻을 수 있도록 implicits를 사용하여 같은 : (사용자 : 사용자) =`아마 당신은 할 수 있습니다 '데프와 (이 (사용자 => 부울) * 조건)으로

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")))