ya_*_*ser 104 sorting scala case-class
我有一个简单的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)
^
Run Code Online (Sandbox Code Playgroud)
我对'黑客'不满意:
scala> l.map(_.toString).sorted.foreach(println)
A(article,2)
A(lines,7)
A(words,50)
Run Code Online (Sandbox Code Playgroud)
J C*_*ell 144
我个人最喜欢的方法是利用为元组提供的隐式排序,因为它清晰,简洁,正确:
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)
}
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为伴侣Ordered定义了一个隐式转换Ordering[T],Ordered[T]该转换属于任何类实现的范围Ordered.Orderings 的隐式s 的存在Tuple使得转换TupleN[...]能够为元组的所有元素Ordered[TupleN[...]]提供隐式Ordering[TN]存在T1, ..., TN,这应该总是这样,因为对没有的数据类型进行排序是没有意义的Ordering.
对于涉及复合排序键的任何排序方案,元组的隐式排序是您的首选:
as.sortBy(a => (a.tag, a.load))
Run Code Online (Sandbox Code Playgroud)
由于这个答案已被证明很受欢迎,我想对其进行扩展,并指出在某些情况下类似以下的解决方案可被视为企业级™:
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)
}
Run Code Online (Sandbox Code Playgroud)
给定es: SeqLike[Employee],es.sorted()将按名称排序,并按es.sorted(Employee.orderingById)ID排序.这有一些好处:
Ordering,因此在大多数情况下直接提供排序可以消除隐式转换.Itt*_*ayD 43
object A {
implicit val ord = Ordering.by(unapply)
}
Run Code Online (Sandbox Code Playgroud)
这样做的好处是,只要A发生变化,它就会自动更新.但是,A的字段需要按照排序使用它们的顺序放置.
om-*_*nom 28
总而言之,有三种方法可以做到这一点:
定义自定义排序.此解决方案的好处是您可以重复使用排序并使用多种方法对同一类的实例进行排序:
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))
Run Code Online (Sandbox Code Playgroud)回答你的问题Scala中是否包含任何标准函数,可以像List((2,1),(1,2))那样做出魔术.
有一组预定义的排序,例如String,最多9个元组等等.
对于案例类,不存在这样的事情,因为它不容易滚动,因为字段名称不是先验的(至少没有宏魔法),并且你不能以不同的方式访问案例类字段name/using product iterator.
小智 7
unapply伴随对象的方法提供从case类到a的转换Option[Tuple],其中Tuple是与case类的第一个参数列表对应的元组.换一种说法:
case class Person(name : String, age : Int, email : String)
def sortPeople(people : List[Person]) =
people.sortBy(Person.unapply)
Run Code Online (Sandbox Code Playgroud)
sortBy方法是一种典型的方法,例如(在tag字段上排序):
scala> l.sortBy(_.tag)foreach(println)
A(article,2)
A(lines,7)
A(words,50)
Run Code Online (Sandbox Code Playgroud)
由于您使用了案例类,因此您可以使用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
Run Code Online (Sandbox Code Playgroud)