说我有一个名单。
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)
是否有任何方法可以让我更改排序而无需编写带有多个sortWith
- if
分支的显式else
方法来处理所有情况?
答案 0 :(得分: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
对象上定义的隐式。
答案 1 :(得分:2)
一种解决方案是扩展否则隐式使用的Tuple2排序。不幸的是,这意味着在代码中写出Tuple2
。
names.sortBy(n => (n.second, n.first))(Ordering.Tuple2(o, o))
答案 2 :(得分:1)
我不是100%确定你认为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(第一个 - 按顺序乘以最大幂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循环。我会假设你不是那种绝望的表现。)