マップに対するパターンマッチ

マップに対するパターンマッチに使える extractor が標準では用意されていないようだし、ぐぐってみてもそれっぽい例が見つからないので、書いてみた。

特定のキー群に対する値を取り出したい場合

class MapValues[A](all: Boolean, keys: A*) {
  def unapplySeq[B](map: Map[A, B]): Option[Seq[B]] = {
    if (all && keys.toSet != map.keySet) None
    else try Some(keys.map(map)) catch { case _: NoSuchElementException => None }
  }
}
val map = Map(1 -> "January", 2 -> "February", 3 -> "March")

// 一部のキー以外は無視したい場合
val false_1_2 = new MapValues(false, 1, 2)
val false_1_2(jan, feb) = map                   // jan = January; feb = February

val false_3 = new MapValues(false, 3)
val false_3(mar) = map                          // mar = March

// 全てのキーを扱いたい場合
val true_3 = new MapValues(true, 3)
val true_3(mar) = map                           // MatchError

val true_3_2_1 = new MapValues(true, 3, 2, 1)
val true_3_2_1(mar, feb, jan) = map             // mar = March; feb = February; jan = January

全てのキーと値を取り出したい場合(キーの昇順に)

object MapElems {
  def unapplySeq[A, B](map: Map[A, B])(implicit ord: Ordering[A]): Option[Seq[(A, B)]] = {
    Some(map.toSeq.sortBy { case (k, v) => k } (ord))
  }
}
val map = Map(1 -> "January", 2 -> "February", 3 -> "March")

val MapElems((one, jan), (two, feb), (three, mar)) = map
// one = 1; jan = January; two = 2; feb = February; three = 3; mar = March

全てのキーと値を取り出したい場合(キーの順序を指定して)

class MapElems[A](ord: Ordering[A]) {
  def unapplySeq[B](map: Map[A, B]): Option[Seq[(A, B)]] = {
    MapElems.unapplySeq(map)(ord)
  }
}
val map = Map(1 -> "January", 2 -> "February", 3 -> "March")

val descending = new MapElems(Ordering[Int].reverse)          // Int の降順で
val descending((three, mar), (two, feb), (one, jan)) = map
// three = 3; mar = March; two = 2; feb = February; one = 1; jan = January