要素がOption型のSeqについて、NoneFirst、NoneLastなソートがしたい場合があったので、いろいろ考えてみました。
(@yasube2613さんの助言を反映しました。コメントありがとうございました。)
今回考えるケース:
// サンプルデータ
// Seq(Some(1)), None, Some(3))
// 得たい結果
// Some(昇順) → Noneのソート
// Seq(Some(1), Some(3), None)
// Some(降順) → Noneのソート
// Seq(Some(3), Some(1), None)
// None → Some(昇順)のソート
// Seq(None, Some(1), Some(3))
// None → Some(降順)のソート
// Seq(None, Some(3), Some(1))
Orderingを定義する
再利用性が高くていい感じですね!
val seq = Seq(Some(1), None, Some(3))
def noneFirstAsc[T](implicit o: Ordering[T]): Ordering[Option[T]] = Ordering.Option
def noneFirstDesc[T](implicit o: Ordering[T]): Ordering[Option[T]] = noneFirstAsc(o.reverse)
def noneLastDesc[T](implicit o: Ordering[T]): Ordering[Option[T]] = noneFirstAsc.reverse
def noneLastAsc[T](implicit o: Ordering[T]): Ordering[Option[T]] = noneLastDesc(o.reverse)
seq.sorted(noneLastAsc[Int]) == Seq(Some(1), Some(3), None)
seq.sorted(noneFirstDesc[Int]) == Seq(Some(3), Some(1), None)
seq.sorted(noneLastDesc[Int]) == Seq(None, Some(1), Some(3))
seq.sorted(noneLastAsc[Int]) == Seq(None, Some(3), Some(1))
ワンライナーで書く
簡潔に書ける場合もあるので、使う場所、頻度によっては、こちらでもいいかもしれません。
val seq = Seq(Some(1), None, Some(3))
// NoneLastでSomeは昇順
seq.partition(_.nonEmpty) match { case (someSeq, noneSeq) =>
someSeq.sorted ++ noneSeq
} == Seq(Some(1), Some(3), None)
// NoneLastでSomeは降順
seq.sorted.reverse == Seq(Some(3), Some(1), None)
// or
seq.partition(_.nonEmpty) match { case (someSeq, noneSeq) =>
someSeq.sorted.reverse ++ noneSeq
} == Seq(Some(3), Some(1), None)
// NoneFirstでSomeは昇順
seq.sorted == Seq(None, Some(1), Some(3))
// or
seq.partition(_.isEmpty) match { case (noneSeq, someSeq) =>
noneSeq ++ someSeq.sorted
} == Seq(None, Some(1), Some(3))
// NoneFirstでSomeは降順
seq.partition(_.isEmpty) match { case (noneSeq, someSeq) =>
noneSeq ++ someSeq.sorted.reverse
} == Seq(None, Some(3), Some(1))
他にいい方法があればコメントください!