为简单案例类定义订购的简单惯用方式 [英] Easy idiomatic way to define Ordering for a simple case class
问题描述
我有一个简单的scala case类实例列表,我想使用list.sorted
以可预测的字典顺序打印它们,但是会收到没有为...定义隐式排序".
I have a list of simple scala case class instances and I want to print them in predictable, lexicographical order using list.sorted
, but receive "No implicit Ordering defined for ...".
是否存在为案例类提供字典编排顺序的隐式?
Is there exist an implicit that provides lexicographical ordering for case classes?
是否有简单的惯用方式将词典编排顺序混合到案例类中?
Is there simple idiomatic way to mix-in lexicographical ordering into case class?
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)
^
我对"hack"不满意:
I am not happy with a 'hack':
scala> l.map(_.toString).sorted.foreach(println)
A(article,2)
A(lines,7)
A(words,50)
推荐答案
我个人最喜欢的方法是使用为元组提供的隐式排序,因为它是清晰,简洁和正确的:
My personal favorite method is to make use of the provided implicit ordering for Tuples, as it is clear, concise, and correct:
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)
}
之所以可行,是因为 Ordered
的伴侣定义了从Ordering[T]
到Ordered[T]
的隐式转换,这在实现Ordered
的任何类的范围内.如果元组的所有元素T1, ..., TN
存在隐式Ordering[TN]
,则对于Tuple
的隐式Ordering
s的存在就可以实现从TupleN[...]
到Ordered[TupleN[...]]
的转换.对没有Ordering
的数据类型进行排序毫无意义.
This works because the companion to Ordered
defines an implicit conversion from Ordering[T]
to Ordered[T]
which is in scope for any class implementing Ordered
. The existence of implicit Ordering
s for Tuple
s enables a conversion from TupleN[...]
to Ordered[TupleN[...]]
provided an implicit Ordering[TN]
exists for all elements T1, ..., TN
of the tuple, which should always be the case because it makes no sense to sort on a data type with no Ordering
.
对于任何涉及复合排序键的排序方案,元组的隐式排序是您的首选:
The implicit ordering for Tuples is your go-to for any sorting scenario involving a composite sort key:
as.sortBy(a => (a.tag, a.load))
由于该答案很受欢迎,因此我想对此进行扩展,并指出在某些情况下可以将类似于以下内容的解决方案视为企业级™:
As this answer has proven popular I would like to expand on it, noting that a solution resembling the following could under some circumstances be considered enterprise-grade™:
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 subclasses 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排序.这有一些好处:
Given es: SeqLike[Employee]
, es.sorted()
will sort by name, and es.sorted(Employee.orderingById)
will sort by id. This has a few benefits:
- 在单个位置将排序定义为可见的代码工件.如果您在许多字段上进行复杂的排序,这将很有用.
- 在scala库中实现的大多数排序功能都是使用
Ordering
实例运行的,因此在大多数情况下,直接提供排序就可以消除隐式转换.
- The sorts are defined in a single location as visible code artifacts. This is useful if you have complex sorts on many fields.
- Most sorting functionality implemented in the scala library operates using instances of
Ordering
, so providing an ordering directly eliminates an implicit conversion in most cases.
这篇关于为简单案例类定义订购的简单惯用方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!