简单的惯用方法来定义一个简单的案例类的订购
我有一个简单的斯卡拉案例类实例的列表,我想打印他们在使用list.sorted
可预测,字典顺序,但收到“没有隐式sorting定义…”。
是否存在一个隐含的提供案例类的词典sorting?
是否有简单的惯用方式将混合词典sorting整合到案例分类中?
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)
我个人最喜欢的方法是利用为元组提供的隐式sorting,因为它是清楚,简洁和正确的:
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
类的范围内。 对于Tuple
的隐式Ordering
的存在使得能够从Tuple
到Ordered[TupleN[...]]
提供对于元组的所有元素T1, ..., TN
存在隐式Ordering[TN]
,应该总是这样,因为对没有sorting的数据types进行sorting是没有意义的。
元组的隐式sorting是任何涉及复合sorting键的sorting情况的前提:
as.sortBy(a => (a.tag, a.load))
由于这个答案已经被certificate是stream行的,所以我想对其进行扩展,指出在某些情况下类似于以下的解决scheme可以被视为企业级™:
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()
将按名称sorting,而es.sorted(Employee.orderingById)
将按IDsorting。 这有几个好处:
- 这些sorting在一个位置被定义为可见的代码构件。 如果你在很多领域有复杂的sorting,这是非常有用的。
- 在scala库中实现的大多数sortingfunction使用
Ordering
实例进行操作,因此在大多数情况下直接提供sorting可以直接消除隐式转换。
object A { implicit val ord = Ordering.by(unapply) }
这具有每当A改变时自动更新的好处。 但是,A的字段需要按顺序将使用它们的顺序放置。
总而言之,有三种方法可以做到这一点:
- 对于一次性sorting,使用.sortBy方法,正如@Shadowlands所示
- @Keith说,重新使用Ordered traitsorting扩展案例类。
-
定义一个自定义的顺序。 这个解决scheme的好处是你可以重用sorting,并有多种方法来sorting同一个类的实例:
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))那样执行魔术。
有一组预定义的顺序 ,例如对于String,最多9个元组的元组等等。
没有这样的事情存在的案例类,因为它是不容易的事情滚落,因为字段名称是不知道先验(至less没有macros魔术),你不能访问大小写字段的方式以外的方式名称/使用产品迭代器。
sortBy方法将是这样做的一个典型方法,例如(按tag
字段sorting):
scala> l.sortBy(_.tag)foreach(println) A(article,2) A(lines,7) A(words,50)
既然你使用了一个case类,你可以像这样使用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
伴随对象的unapply
方法提供了从你的case类到一个Option[Tuple]
,其中Tuple
是对应于case类的第一个参数列表的元组。 换一种说法:
case class Person(name : String, age : Int, email : String) def sortPeople(people : List[Person]) = people.sortBy(Person.unapply)