Posted at

ScalaでList[LocalDateTime]をソートする


LocalDateTimeリストのsortedでエラーが出た

LocalDateTimeのリストをsortedでソートしようとしたら以下のようなエラーが出た

List(time3, time1, time2)

> List[java.time.LocalDateTime] = List(2018-11-23T19:18:28.758, 2018-11-23T19:18:22.479, 2018-11-23T19:18:25.296)

List(time3, time1, time2).sorted // time*はLocalDateTime
> error: No implicit Ordering defined for java.time.LocalDateTime.
List(time1, time2, time3).sorted

ガッデム。

REPLでとりあえずsortedの定義を出す

def sorted[B >: java.time.LocalDateTime](implicit ord: scala.math.Ordering[B]): List[java.time.LocalDateTime]

Ordering型クラスのLocalDateTimeインスタンスを定義すれば良さそうだ

実装が必要なメソッドはcompare

 /** Returns an integer whose sign communicates how x compares to y.

*
* The result sign has the following meaning:
*
* - negative if x < y
* - positive if x > y
* - zero otherwise (if x == y)
*/

def compare(x: T, y: T): Int

compareを正しく動作させるためには

※第一引数xが第二引数y

xがyより小さい時、負の数を返す

xがyより大きい時、正の数を返す

xとyが同じ時、0を返す

このように実装すれば良いらしい


インスタンスを定義

compareToはLocalDateTimeのメソッドで、都合の良いことにxがyより小さい時-1、xがyより大きい時1、同じ時0を返してくれるのでそれをそのまま利用する

implicit val localDateTimeOrdering = new Ordering[LocalDateTime] {

def compare(x: LocalDateTime, y: LocalDateTime): Int = {
x.compareTo(y)
}
}

このインスタンスをsortedを利用するスコープ内で読み込めば最初エラーになっていた処理がちゃんとソートされるようになる

List(time3, time1, time2).sorted

> List[java.time.LocalDateTime] = List(2018-11-23T19:18:22.479, 2018-11-23T19:18:25.296, 2018-11-23T19:18:28.758)


おまけ

Orderingのcompareを正しく動作させるには負の数、正の数を返すとあったが-100, 50, 0を返すような場合でも正しく動作するのかやってみた

case class Player(number: Int, name: String)

val player1 = Player(13, "Alisson")
val player2 = Player(4, "Virgil van Dijk")
val player3 = Player(14, "Henderson")
val player4 = Player(11, "Salah")

val players = List(player1, player2, player3, player4)
// ソートはnumber順
players.sorted // この時点ではエラー error: No implicit Ordering defined for Player. players.sorted

// Playerインスタンスの定義
implicit val playerOrdering = new Ordering[Player] {
def compare(p1: Player, p2: Player): Int = {
if (p1.number < p2.number) -100 // -1ではなく-100
else if (p1.number > p2.number) 50 // 1ではなく50
else 0
}
}

players.sorted
> List[Player] = List(Player(4,Virgil van Dijk), Player(11,Salah), Player(13,Alisson), Player(14,Henderson))

このように-100, 50, 0を返すような実装でも動作する(気持ち悪いが)

※LocalDateTimeのcompareToメソッドのように-1, 1, 0を返すのが作法ぽい


参照

型クラスをOrderingを用いて説明してみる