case classとList/Map/Tupleの相互変換

Programming Scala

case classとコレクションを相互変換したいことがたまにあるので方法をまとめておきます。

もちろんやり方は一つではないので一例です。

準備

scala 2.9.2で確認

scala> case class Foo(i: Int, s: String)
defined class Foo

変換方法

List

Listにするのは簡単ですが、case classに戻すのはリフレクションが必須でasInstanceOfが頻発して嫌な感じですね。

productIteratorの段階で型情報が失われてしまうのが原因ではないかと思います。

ref: scala - Instantiating a case class from a list of parameters - Stack Overflow

case class -> List

scala> val l = f.productIterator.toList
l: List[Any] = List(1, bar)

List -> case class

scala> Foo.getClass.getMethods.find(_.getName == "apply").get.invoke(Foo, l.map(_.asInstanceOf[AnyRef]):_*).asInstanceOf[Foo]
res1: Foo = Foo(1,bar

Map

Mapへの変換はリフレクションでフィールド名を取得して、上記の値のリストにzipすればオッケーです。

Field.getを利用して直接value込のtupleを作る方法もあるようです。URL先参照。

case classへの変換はvalues.toListすれば後はListを同じ方法で。

ref: Case class to map in Scala - Stack Overflow

case class -> Map

scala> val m = f.getClass.getDeclaredFields.map(_.getName).zip(f.productIterator.toList).toMap
m: scala.collection.immutable.Map[java.lang.String,Any] = Map(i -> 1, s -> bar)

Map -> case class

scala> Foo.getClass.getMethods.find(_.getName == "apply").get.invoke(Foo, m.values.toList.map(_.asInstanceOf[AnyRef]):_*).asInstanceOf[Foo]
res2: Foo = Foo(1,bar)

Tuple

Tupleはcase classと両者ともProductをmix-inされているなど意味的に近いので、変換は行きも帰りも素直にできます。

case class -> Tuple

scala> val t = Foo.unapply(f).get
t: (Int, String) = (1,bar)

Tuple -> case class

scala> Foo.tupled(t)
res3: Foo = Foo(1,bar)

Comments