我有一个简单的 scala 案例类实例列表,我想使用list.sorted
以可的词典顺序打印它们,但收到“没有为...定义隐式排序”。
是否存在为案例类提供词典排序的隐式?
是否有简单的惯用方法将字典排序混合到案例类中?
scala> case class A(tag:String, load:Int)
scala> val l = List(A("words",50),A("article",2),A("lines",7))
scala> l.sorted.foreach(println)
<console>:11: error: No implicit Ordering defined for A.
l.sorted.foreach(println)
^
我对“黑客”不满意:
scala> l.map(_.toString).sorted.foreach(println)
A(article,2)
A(lines,7)
A(words,50)
我个人最喜欢的方法是使用为元组提供的隐式排序,因为它是清晰,简洁和正确的:
case class A(tag: String, load: Int) extends Ordered[A] {
// Required as of Scala 2.11 for reasons unknown - the companion to Ordered
// should already be in implicit scope
import scala.math.Ordered.orderingToOrdered
def compare(that: A): Int = (this.tag, this.load) compare (that.tag, that.load)
}
这是因为companion to Ordered
定义了从Ordering[T]
到Ordered[T]
的隐式转换,该转换在实现Ordered
的任何类的范围内。Tuple
s 的隐式Ordering
s 的存在使TupleN[...]
到Ordered[TupleN[...]]
的所有元素都
元组的隐式排序是涉及复合排序键的任何排序方案的首选:
as.sortBy(a => (a.tag, a.load))
由于这个答案已被证明是受欢迎的,我想对其进行扩展,并指出在某些情况下类似于以下内容的解决方案可以被视为企业级 ™:
case class Employee(id: Int, firstName: String, lastName: String)
object Employee {
// Note that because `Ordering[A]` is not contravariant, the declaration
// must be type-parametrized in the event that you want the implicit
// ordering to apply to subcl of `Employee`.
implicit def orderingByName[A <: Employee]: Ordering[A] =
Ordering.by(e => (e.lastName, e.firstName))
val orderingById: Ordering[Employee] = Ordering.by(e => e.id)
}
给定es: SeqLike[Employee]
,es.sorted()
将按名称排序,es.sorted(Employee.orderingById)
将按 id 排序。这有几个好处:
排序在单个位置定义为可见代码工件。如果您在许多字段上有复杂的排序,这将很有用。
scala 库中实现的大多数排序功能使用Ordering
的实例进行操作,因此在大多数情况下,直接提供排序可以消除隐式转换。
object A {
implicit val ord = Ordering.by(unapply)
}
这样做的好处是,每当 A 更改时,它都会自动更新。但是,A 的字段需要按照排序使用它们的顺序放置。
总结一下,有三种方法可以做到这一点:
对于一次性排序,请使用.sortBy 方法,如 @ Shadowlands 所示
如 @ Keith 所说,用于重用具有 Ordered 特征的排序扩展案例类。
定义自定义排序。此解决方案的好处是您可以重用排序,并有多种方式对同一类的实例进行排序:
case class A(tag:String, load:Int)
object A {
val lexicographicalOrdering = Ordering.by { foo: A =>
foo.tag
}
val loadOrdering = Ordering.by { foo: A =>
foo.load
}
}
implicit val ord = A.lexicographicalOrdering
val l = List(A("words",1), A("article",2), A("lines",3)).sorted
// List(A(article,2), A(lines,3), A(words,1))
// now in some other scope
implicit val ord = A.loadOrdering
val l = List(A("words",1), A("article",2), A("lines",3)).sorted
// List(A(words,1), A(article,2), A(lines,3))
回答您的问题Scala 中是否包含任何标准函数,可以像 List((2,1),(1,2)).sorted
有一组predefined orderings,例如对于 String,元组高达 9 arity 等等。
案例类不存在这样的事情,因为它不容易滚下来,因为字段名称不知道先验(至少没有宏魔术),你不能以其他方式访问案例类字段通过名称 / 使用产品迭代器。
伴随对象的unapply
方法提供了从您的 case 类到Option[Tuple]
的转换,其中Tuple
是对应于 case 类的第一个参数列表的元组。
case class Person(name : String, age : Int, email : String)
def sortPeople(people : List[Person]) =
people.sortBy(Person.unapply)
本站系公益性非盈利分享网址,本文来自用户投稿,不代表码文网立场,如若转载,请注明出处
评论列表(73条)