単純な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)
私の個人的なお気に入りの方法は、明確で簡潔で正しいので、提供されている暗黙的なタプルの順序を使用することです:
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
へのコンパニオン は、Ordered
を実装するクラスのスコープ内にあるOrdering[T]
からOrdered[T]
への暗黙的な変換を定義するため、機能します。 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
のインスタンスを使用して動作するため、ほとんどの場合、順序付けを直接提供することで暗黙的な変換を排除します。object A {
implicit val ord = Ordering.by(unapply)
}
これには、Aが変更されるたびに自動的に更新されるという利点があります。ただし、Aのフィールドは、順序付けで使用される順序で配置する必要があります。
要約すると、これを行うには3つの方法があります。
カスタム順序を定義します。このソリューションの利点は、順序を再利用でき、同じクラスのインスタンスをソートする複数の方法があることです。
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))
あなたの質問に答えるList((2,1)、(1,2))。sortedのような魔法を行うことができるScalaに含まれる標準関数はありますか?
事前定義された順序 のセットがあります。文字列の場合、最大9個のアリティまでのタプルなど。
フィールド名が事前に知られていないため(少なくともマクロの魔法がなければ)ロールオフするのは簡単ではないので、ケースクラスにはそのようなものは存在しません。あなたは以外の方法でケースクラスフィールドにアクセスすることはできません名前/使用する製品イテレータ。
コンパニオンオブジェクトのunapply
メソッドは、ケースクラスからOption[Tuple]
への変換を提供します。ここで、Tuple
は、ケースクラスの最初の引数リストに対応するタプルです。言い換えると:
case class Person(name : String, age : Int, email : String)
def sortPeople(people : List[Person]) =
people.sortBy(Person.unapply)
SortByメソッドは、これを行う典型的な方法の1つです。たとえば(tag
フィールドでソート):
scala> l.sortBy(_.tag)foreach(println)
A(article,2)
A(lines,7)
A(words,50)
ケースクラスを使用したため、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