2011-02-18 6 views
4

나는 이름 목록이 있다고 가정 해 봅시다.스칼라에서 Seq.sortBy 확장하기

case class Name(val first: String, val last: String) 

val names = Name("c", "B") :: Name("b", "a") :: Name("a", "B") :: Nil 

이제 성을 기준으로 목록을 정렬하려는 경우 (그리고 이름이 충분하지 않은 경우 이름으로) 쉽게 정렬 할 수 있습니다.

names.sortBy(n => (n.last, n.first)) 
// List[Name] = List(Name(a,B), Name(c,B), Name(b,a)) 

그러나 문자열의 다른 데이터 정렬을 기반으로이 목록을 정렬하려면 어떻게해야합니까?

불행하게도, 다음은 작동하지 않습니다

val o = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) } 
names.sortBy(n => (n.last, n.first))(o) 
// error: type mismatch; 
// found : java.lang.Object with Ordering[String] 
// required: Ordering[(String, String)] 
// names.sortBy(n => (n.last, n.first))(o) 

날 여러 함께 if 명시 적 sortWith 방법을 작성하지 않고도 순서를 변경할 수있는 방법이 - 모든 처리하기 위해 else 가지 케이스?

답변

2

다른 해결책은 암시 적으로 사용되는 Tuple2 순서를 확장하는 것입니다. 불행히도 이것은 코드에 Tuple2을 쓰는 것을 의미합니다.

names.sortBy(n => (n.second, n.first))(Ordering.Tuple2(o, o)) 
1

나는 어떤 방법으로 collator을 가져야하는지 확실하지 않습니다.

하지만이 경우 클래스의 순서 정의하면 당신은 가장 유연성이 있습니다

val o = new Ordering[Name]{ 
    def compare(a: Name, b: Name) = 
    3*math.signum(collator.compare(a.last,b.last)) + 
    math.signum(collator.compare(a.first,b.first)) 
} 
names.sorted(o) 

을하지만 당신은 또한 이름 순서에 문자열 순서에서 암시 적 변환 제공 할 수

def ostring2oname(os: Ordering[String]) = new Ordering[Name] { 
    def compare(a: Name, b: Name) = 
    3*math.signum(os.compare(a.last,b.last)) + math.signum(os.compare(a.first,b.first)) 
} 

다음은 이름 정렬 할 문자열의 순서를 사용할 수 있습니다

def oo = new Ordering[String] { 
    def compare(x: String, y: String) = x.length compare y.length 
} 
val morenames = List("rat","fish","octopus") 

scala> morenames.sorted(oo) 
res1: List[java.lang.String] = List(rat, fish, octopus) 

편집 : 손쉬운 트릭이 있는데, N 개의 항목으로 주문하려는 경우 이미 비교를 사용하고 있다면 각 항목에 3^k를 곱하면됩니다. to-order에 3의 가장 큰 거듭 제곱을 곱한 값)을 더한다.


당신의 비교는 매우 경우 시간이 많이 소요, 당신은 쉽게 계단식 비교 추가 할 수 있습니다

class CascadeCompare(i: Int) { 
    def tiebreak(j: => Int) = if (i!=0) i else j 
} 
implicit def break_ties(i: Int) = new CascadeCompare(i) 

다음

def ostring2oname(os: Ordering[String]) = new Ordering[Name] { 
    def compare(a: Name, b: Name) = 
    os.compare(a.last,b.last) tiebreak os.compare(a.first,b.first) 
} 

(단지 둥지 그들 x tiebreak (y tiebreak (z tiebreak w))) 그래서 당신에게주의를 암시 적 변환을 여러 번 연속해서하지 마십시오).

(빠른 비교가 필요한 경우 직접 손으로 작성하거나 배열에 순서를 압축하고 while 루프를 사용해야합니다. 필자는 성능이 절실히 필요하지 않다고 가정합니다.)

+0

음, 잘 모르겠습니다. 3^k로 곱하면 똑똑해 보이지만 -1/0/+ 1 규칙을 따르지 않으면 무언가가 당연히 작동하지 않습니다. 또한, 차이점이 발견되면 Tuple 비교가 중지되는 반면, 모든 비교를 즉시 평가합니다. – Debilski

+1

@Debilski - Whoops,'math.signum' 래퍼를 잊어 버렸습니다! 그리고 예, 그것은 완전한 평가를합니다. 속도에는 이상적이지 않습니다. 그렇습니다. –

+1

'collator'는'java.text.Collator'의 인스턴스라고 가정했습니다. –

4

음, 이것은 거의 트릭을 수행합니다 한편

names.sorted(o.on((n: Name) => n.last + n.first)) 

을, 당신은뿐만 아니라이 작업을 수행 할 수 있습니다

implicit val o = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) } 
names.sortBy(n => (n.last, n.first)) 

이 로컬로 정의 된보다 우선합니다 암시 적 정의 Ordering 개체에서