15

私がこのクラスを持っているとしましょう

case class Test (id: Long, name: String)

そしてこのクラスのインスタンス:

Test :
id -> 1
name -> toto

次のようにMap[String、String]を作成したいと思います。

Map( "id" -> "1", "name" -> "toto")

私の質問は:このTestのインスタンスをMap [String、String]に変換する方法はありますか?私はこのような方法を使用することを避けたいです:

def createMap(instance: Test): Map[String, String] = {
    val map = new Map[String, String]
    map.put("id", instance.id.toString)
    map.put("name", instance.name)
    map
}

Scalaにそうする方法がない場合、クラスのプロパティを反復処理する方法はありますか?たぶん私はそうするためのジェネリック関数を作成することができます:

def createMap(instance: T): Map[String, String] = {
   val map = new Map[String, String]
   //pseudocode 
   for  ((name, value) <- instance.getClassProperties.getValues) {
      case value.isInstanceOf[String] : map.push(name, value)
      case _ : map.push(name, value.toString)
    }
    map
}

それは可能ですか?良い例/リンクがあれば、興味があります。

4

3 に答える 3

21

はい、可能です。Scala 2.10以降、リフレクションを使用できます。

あなたが持っていると仮定して:

val test = Test(23423, "sdlkfjlsdk")

以下はあなたが望むものを手に入れるでしょう:

import reflect.runtime.universe._
import reflect.runtime.currentMirror

val r = currentMirror.reflect(test)
r.symbol.typeSignature.members.toStream
  .collect{case s : TermSymbol if !s.isMethod => r.reflectField(s)}
  .map(r => r.symbol.name.toString.trim -> r.get.toString)
  .toMap

ケースクラスのフィールド値を単純に反復処理する.productIteratorには、そのインスタンスで使用します。

于 2012-10-09T10:35:20.607 に答える
11

あなたが扱っているトピックはStackOverFlowで信じられないほど繰り返されるようになっており、タイプセーフな実装が必要な場合、問題は簡単ではありません。

この問題を解決する1つの方法は、(提案されているように)リフレクションを使用することですが、私は個人的に型システムと暗黙を使用することを好みます。

非常に賢い人によって開発された有名なライブラリがあり、ケースクラスをタイプセーフな異種リストに変換したり、「拡張可能なレコード」を実装するために使用できる異種マップを作成したりするなどの高度な操作を実行できます。ライブラリはShapelessと呼ばれ、ここに1つの例があります。

object RecordExamples extends App {
  import shapeless._
  import HList._
  import Record._

  object author  extends Field[String]  { override def toString = "Author" }
  object title   extends Field[String]  { override def toString = "Title" }
  object id      extends Field[Int]     { override def toString = "ID" }
  object price   extends Field[Double]  { override def toString = "Price" }
  object inPrint extends Field[Boolean] { override def toString = "In print" }

  def printBook[B <: HList](b : B)(implicit tl : ToList[B, (Field[_], Any)]) = {
    b.toList foreach { case (field, value) => println(field+": "+value) }
    println
  }

  val book =
    (author -> "Benjamin Pierce") ::
    (title  -> "Types and Programming Languages") ::
    (id     ->  262162091) ::
    (price  ->  44.11) ::
    HNil

  printBook(book)

  // Read price field
  val currentPrice = book.get(price)  // Static type is Double
  println("Current price is "+currentPrice)
  println

  // Update price field, relying on static type of currentPrice
  val updated = book + (price -> (currentPrice+2.0))
  printBook(updated)

  // Add a new field
  val extended = updated + (inPrint -> true)
  printBook(extended)

  // Remove a field
  val noId = extended - id 
  printBook(noId)
}

Bookは、オブジェクトをキーとして使用してインデックスを作成できるタイプセーフマップのように動作します。詳細を知りたい場合は、次の投稿が適切なエントリポイントになる可能性があります。

HListsは、タプルを書くための複雑な方法にすぎませんか?

于 2012-10-09T10:13:48.627 に答える
0

(の実装として)を開始するとScala 2.13、フィールドの名前に対してイテレータを返すメソッドが提供されます。case classesProductproductElementNames

で取得したフィールド値でフィールド名を圧縮するproductIteratorことにより、一般的に関連を取得し、関連するMap[String, Any]値で値をマッピングすることができます。toStringMap[String, String]

// case class Test(id: Long, name: String)
// val x = Test(1, "todo")
(x.productElementNames zip x.productIterator.map(_.toString)).toMap
// Map[String,String] = Map("id" -> "1", "name" -> "todo")
于 2019-03-10T13:56:55.643 に答える