scala 为简单案例类定义排序的简单惯用方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19345030/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Easy idiomatic way to define Ordering for a simple case class
提问by ya_pulser
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 ...".
我有一个简单的 Scala 案例类实例列表,我想使用 以可预测的字典顺序打印它们list.sorted,但收到“没有为...定义隐式排序”。
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)
^
I am not happy with a 'hack':
我对“黑客”不满意:
scala> l.map(_.toString).sorted.foreach(println)
A(article,2)
A(lines,7)
A(words,50)
回答by J Cracknell
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)
}
This works because the companion to Ordereddefines an implicit conversion from Ordering[T]to Ordered[T]which is in scope for any class implementing Ordered. The existence of implicit Orderings for Tuples enables a conversion from TupleN[...]to Ordered[TupleN[...]]provided an implicit Ordering[TN]exists for all elements T1, ..., TNof the tuple, which should always be the case because it makes no sense to sort on a data type with no Ordering.
这是有效的,因为伴随 toOrdered定义了一个隐式转换 from Ordering[T]toOrdered[T]的任何类实现Ordered. 如果元组的所有元素都隐式存在Ordering,则Tuples的隐式s的存在可以实现从TupleN[...]到的转换,这种情况应该始终如此,因为对没有 no 的数据类型进行排序是没有意义的。Ordered[TupleN[...]]Ordering[TN]T1, ..., TNOrdering
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)
}
Given es: SeqLike[Employee], es.sorted()will sort by name, and es.sorted(Employee.orderingById)will sort by id. This has a few benefits:
Given es: SeqLike[Employee],es.sorted()将按名称排序,并将es.sorted(Employee.orderingById)按 id 排序。这有几个好处:
- 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.
- 排序在单个位置定义为可见的代码工件。如果您对许多字段进行复杂排序,这将非常有用。
- scala 库中实现的大多数排序功能都使用 的实例进行操作
Ordering,因此在大多数情况下直接提供排序消除了隐式转换。
回答by IttayD
object A {
implicit val ord = Ordering.by(unapply)
}
This has the benefit that it is updated automatically whenever A changes. But, A's fields need to be placed in the order by which the ordering will use them.
这样做的好处是它会在 A 更改时自动更新。但是,A 的字段需要按照排序使用它们的顺序放置。
回答by om-nom-nom
To summarize, there are three ways to do this:
总结一下,有三种方法可以做到这一点:
- For one-off sorting use .sortBy method, as @Shadowlands have showed
- For reusing of sorting extend case class with Ordered trait, as @Keith said.
Define a custom ordering. The benefit of this solution is that you can reuse orderings and have multiple ways to sort instances of the same class:
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))
- 对于一次性排序使用 .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))
Answering your question Is there any standard function included into the Scala that can do magic like List((2,1),(1,2)).sorted
回答您的问题Scala 中是否包含任何标准函数可以执行像 List((2,1),(1,2)).sorted 这样的魔术
There is a set of predefined orderings, e.g. for String, tuples up to 9 arity and so on.
有一组预定义的排序,例如字符串、最多 9 个元组等。
No such thing exists for case classes, since it is not easy thing to roll off, given that field names are not known a-priori (at least without macros magic) and you can't access case class fields in a way other than by name/using product iterator.
案例类不存在这样的东西,因为它不是一件容易的事情,因为字段名称是先验不知道的(至少没有宏魔法)并且您不能以除通过之外的方式访问案例类字段名称/使用产品迭代器。
回答by Joakim Ahnfelt-R?nne
The unapplymethod of the companion object provides a conversion from your case class to an Option[Tuple], where the Tupleis the tuple corresponding to the first argument list of the case class. In other words:
unapply伴随对象的方法提供了从 case 类到 an 的转换Option[Tuple],其中Tuple是对应于 case 类的第一个参数列表的元组。换句话说:
case class Person(name : String, age : Int, email : String)
def sortPeople(people : List[Person]) =
people.sortBy(Person.unapply)
回答by Shadowlands
The sortBy method would be one typical way of doing this, eg (sort on tagfield):
sortBy 方法将是执行此操作的一种典型方法,例如(按tag字段排序):
scala> l.sortBy(_.tag)foreach(println)
A(article,2)
A(lines,7)
A(words,50)
回答by Keith Pinson
Since you used a case classyou could extend with Orderedlike such:
由于您使用了案例类,您可以像这样使用Ordered 进行扩展:
case class A(tag:String, load:Int) extends Ordered[A] {
def compare( a:A ) = tag.compareTo(a.tag)
}
val ls = List( A("words",50), A("article",2), A("lines",7) )
ls.sorted

