我有一个简单的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)
答案 0 :(得分:137)
我个人最喜欢的方法是利用为元组提供的隐式排序,因为它清晰,简洁,正确:
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
的类。 Ordering
s的隐式Tuple
s的存在允许从TupleN[...]
转换为Ordered[TupleN[...]]
,前提是所有元素Ordering[TN]
都存在隐式T1, ..., TN
元组,应始终如此,因为对没有Ordering
的数据类型进行排序是没有意义的。
元组的隐式排序是涉及复合排序键的任何排序方案的首选:
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 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排序。这有一些好处:
Ordering
的实例进行操作,因此在大多数情况下直接提供排序会消除隐式转换。答案 1 :(得分:36)
object A {
implicit val ord = Ordering.by(unapply)
}
这样做的好处是,只要A发生变化,它就会自动更新。但是,A的字段需要按照排序使用它们的顺序放置。
答案 2 :(得分:26)
总而言之,有三种方法可以做到这一点:
定义自定义排序。此解决方案的好处是您可以重复使用排序并使用多种方法对同一类的实例进行排序:
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))那样做魔术。排序
有一组predefined orderings,例如for String,元组最多9个等等。
对于案例类没有这样的东西,因为滚动是不容易的,因为字段名称不是先验的(至少没有宏魔法)并且你不能以某种方式访问案例类字段除了名字/使用产品迭代器。
答案 3 :(得分:7)
随播对象的unapply
方法提供从案例类到Option[Tuple]
的转换,其中Tuple
是与案例类的第一个参数列表对应的元组。换句话说:
case class Person(name : String, age : Int, email : String)
def sortPeople(people : List[Person]) =
people.sortBy(Person.unapply)
答案 4 :(得分:6)
sortBy方法是一种典型的方法,例如(在tag
字段上排序):
scala> l.sortBy(_.tag)foreach(println)
A(article,2)
A(lines,7)
A(words,50)
答案 5 :(得分:5)
由于您使用了案例类,您可以使用 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
答案 6 :(得分:0)
我个人最喜欢的方法是使用 SAM(单一抽象方法)和 2.12,如下面的示例所述:
case class Team(city:String, mascot:String)
//Create two choices to sort by, city and mascot
object MyPredef3 {
// Below used in 2.11
implicit val teamsSortedByCity: Ordering[Team] = new Ordering[Team] {
override def compare(x: Team, y: Team) = x.city compare y.city
}
implicit val teamsSortedByMascot: Ordering[Team] = new Ordering[Team] {
override def compare(x: Team, y: Team) = x.mascot compare y.mascot
}
/*
Below used in 2.12
implicit val teamsSortedByCity: Ordering[Team] =
(x: Team, y: Team) => x.city compare y.city
implicit val teamsSortedByMascot: Ordering[Team] =
(x: Team, y: Team) => x.mascot compare y.mascot
*/
}
object _6OrderingAList extends App {
//Create some sports teams
val teams = List(Team("Cincinnati", "Bengals"),
Team("Madrid", "Real Madrid"),
Team("Las Vegas", "Golden Knights"),
Team("Houston", "Astros"),
Team("Cleveland", "Cavaliers"),
Team("Arizona", "Diamondbacks"))
//import the implicit rule we want, in this case city
import MyPredef3.teamsSortedByCity
//min finds the minimum, since we are sorting
//by city, Arizona wins.
println(teams.min.city)
}